Mitt förslag är att lagra min/max/total för alla intervaller du är intresserad av och uppdatera det för nuvarande med varje ankommande datapunkt. För att undvika nätverkslatens när du läser tidigare data för jämförelse kan du göra det helt på Redis-servern med Lua-skript.
En nyckel per datapunkt (eller, ännu värre, per datapunktsfält) kommer att förbruka för mycket minne. För bästa resultat bör du gruppera det i små listor/hashar (se http://redis.io/topics/memory-optimization). Redis tillåter bara en nivå av kapsling i dess datastrukturer:om din data har flera fält och du vill lagra mer än ett objekt per nyckel måste du på något sätt koda det själv. Lyckligtvis inkluderar standard Redis Lua-miljö msgpack-stöd som är ett mycket effektivt binärt JSON-liknande format. JSON-poster i ditt exempel kodade med msgpack "as is" kommer att vara 52-53 byte långa. Jag föreslår att gruppera efter tid så att du har 100-1000 poster per nyckel. Antag att en minuts intervall uppfyller detta krav. Då skulle nyckelschemat vara så här:
YYmmddHHMMSS
— en hash från tid
till msgpack-kodade datapunkter för den givna minuten.5m:YYmmddHHMM
, 1h:YYmmddHH
, 1d:YYmmdd
— fönsterdatahaschar som innehåller min
, max
, sum
fält.
Låt oss titta på ett exempel på ett Lua-skript som accepterar en datapunkt och uppdaterar alla nycklar vid behov. På grund av hur Redis skript fungerar måste vi uttryckligen skicka namnen på alla nycklar som kommer att nås av skriptet, det vill säga livedata och alla tre fönsternycklarna. Redis Lua har också ett JSON-analysbibliotek tillgängligt, så låt oss för enkelhetens skull anta att vi bara skickar det JSON-ordboken. Det betyder att vi måste analysera data två gånger:på applikationssidan och på Redis-sidan, men prestandaeffekterna av det är inte tydliga.
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)
Denna inställning kan göra tusentals uppdateringar per sekund, inte särskilt sugen på minne, och fönsterdata kan hämtas direkt.
UPPDATERING:På minnesdelen är 50-60 byte per punkt fortfarande mycket om du vill lagra mer några miljoner. Med den här typen av data tror jag att du kan få så lite som 2-3 byte per punkt med hjälp av anpassat binärt format, deltakodning och efterföljande komprimering av bitar med något som snappy. Det beror på dina krav, om det är värt att göra detta.