Mit forslag er at gemme min/max/total for alle intervaller, du er interesseret i, og opdatere det for nuværende med hvert ankommende datapunkt. For at undgå netværksforsinkelse, når du læser tidligere data til sammenligning, kan du gøre det helt inde på Redis-serveren ved hjælp af Lua-scripting.
En nøgle pr. datapunkt (eller endnu værre, pr. datapunktfelt) kommer til at forbruge for meget hukommelse. For de bedste resultater bør du gruppere det i små lister/hashes (se http://redis.io/topics/memory-optimization). Redis tillader kun ét niveau af indlejring i dets datastrukturer:Hvis dine data har flere felter, og du vil gemme mere end ét element pr. nøgle, skal du på en eller anden måde selv kode det. Heldigvis inkluderer standard Redis Lua-miljø msgpack-understøttelse, som er et meget effektivt binært JSON-lignende format. JSON-indgange i dit eksempel kodet med msgpack "som den er" vil være 52-53 bytes lange. Jeg foreslår at gruppere efter tid, så du har 100-1000 poster pr. nøgle. Antag, at et minuts interval passer til dette krav. Så ville nøgleskemaet være sådan her:
YYmmddHHMMSS
— en hash fra tid
til msgpack-kodede datapunkter for det givne minut.5m:YYmmddHHMM
, 1h:YYmmddHH
, 1d:YYmmdd
— vinduesdatahash, der indeholder min
, max
, sum
felter.
Lad os se på et eksempel på et Lua-script, der accepterer ét datapunkt og opdaterer alle nøgler efter behov. På grund af den måde Redis scripting fungerer på, er vi nødt til eksplicit at videregive navnene på alle nøgler, som scriptet får adgang til, dvs. live-dataene og alle tre vinduesnøgler. Redis Lua har også et JSON-parsing-bibliotek tilgængeligt, så lad os for nemhedens skyld antage, at vi bare videregiver den JSON-ordbog. Det betyder, at vi skal parse data to gange:på applikationssiden og på Redis-siden, men ydeevneeffekterne af det er ikke klare.
local function update_window(winkey, price, amount)
local windata = redis.call('HGETALL', winkey)
if price > tonumber(windata.max or 0) then
redis.call('HSET', winkey, 'max', price)
end
if price < tonumber(windata.min or 1e12) then
redis.call('HSET', winkey, 'min', price)
end
redis.call('HSET', winkey, 'sum', (windata.sum or 0) + amount)
end
local currkey, fiveminkey, hourkey, daykey = unpack(KEYS)
local data = cjson.decode(ARGV[1])
local packed = cmsgpack.pack(data)
local tid = data.tid
redis.call('HSET', currkey, tid, packed)
local price = tonumber(data.price)
local amount = tonumber(data.amount)
update_window(fiveminkey, price, amount)
update_window(hourkey, price, amount)
update_window(daykey, price, amount)
Denne opsætning kan udføre tusindvis af opdateringer i sekundet, ikke særlig sulten på hukommelsen, og vinduesdata kan hentes øjeblikkeligt.
OPDATERING:På hukommelsesdelen er 50-60 bytes per punkt stadig meget, hvis du vil gemme mere et par millioner. Med denne slags data tror jeg, at du kan få så lavt som 2-3 bytes pr. punkt ved at bruge brugerdefineret binært format, delta-kodning og efterfølgende komprimering af bidder ved hjælp af noget som snappy. Det afhænger af dine krav, om det er værd at gøre dette.