Můj návrh je uložit min/max/celkem pro všechny intervaly, které vás zajímají, a aktualizovat je pro aktuální s každým příchozím datovým bodem. Abyste se vyhnuli latenci sítě při čtení předchozích dat pro porovnání, můžete to provést výhradně na serveru Redis pomocí skriptování Lua.
Jeden klíč na datový bod (nebo ještě hůře na pole datového bodu) spotřebuje příliš mnoho paměti. Chcete-li dosáhnout nejlepších výsledků, měli byste je seskupit do malých seznamů/hash (viz http://redis.io/topics/memory-optimization). Redis umožňuje pouze jednu úroveň vnoření ve svých datových strukturách:pokud vaše data mají více polí a chcete uložit více než jednu položku na klíč, musíte je nějak sami zakódovat. Naštěstí standardní prostředí Redis Lua obsahuje podporu msgpack, což je velmi účinný binární formát podobný JSON. Záznamy JSON ve vašem příkladu zakódované pomocí msgpack „tak jak jsou“ budou mít délku 52–53 bajtů. Navrhuji seskupení podle času, abyste měli 100-1000 záznamů na klíč. Předpokládejme, že tomuto požadavku vyhovuje jednominutový interval. Pak by schéma klíčování vypadalo takto:
YYmmddHHMMSS
— hash z tid
na datové body zakódované pomocí msgpack pro danou minutu.5m:YYmmddHHMM
, 1h:YYmmddHH
, 1d:YYmmdd
— hash dat okna, které obsahují min
, max
, sum
pole.
Podívejme se na ukázkový skript Lua, který přijme jeden datový bod a podle potřeby aktualizuje všechny klíče. Vzhledem k tomu, jak skriptování Redis funguje, musíme explicitně předat jména všech klíčů, ke kterým bude skript přistupovat, tedy živá data a všechny tři klíče oken. Redis Lua má k dispozici také knihovnu pro analýzu JSON, takže pro jednoduchost předpokládejme, že jí předáme slovník JSON. To znamená, že musíme data analyzovat dvakrát:na straně aplikace a na straně Redis, ale efekty na výkon nejsou jasné.
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)
Toto nastavení může provádět tisíce aktualizací za sekundu, není příliš náročné na paměť a data okna lze načíst okamžitě.
AKTUALIZACE:Pokud jde o paměť, 50-60 bajtů na bod je stále hodně, pokud chcete uložit více o několik milionů. S tímto druhem dat si myslím, že můžete získat až 2-3 bajty na bod pomocí vlastního binárního formátu, delta kódování a následné komprese kusů pomocí něčeho jako snappy. Záleží na vašich požadavcích, zda se vám to vyplatí.