Моето предложение е да съхранявате min/max/total за всички интервали, които ви интересуват, и да го актуализирате за текущи с всяка пристигаща точка от данни. За да избегнете забавяне на мрежата при четене на предишни данни за сравнение, можете да го направите изцяло в Redis сървър, като използвате Lua скриптове.
Един ключ на точка от данни (или, още по-лошо, на поле за точка от данни) ще изразходва твърде много памет. За най-добри резултати трябва да го групирате в малки списъци/хешове (вижте http://redis.io/topics/memory-optimization). Redis позволява само едно ниво на влагане в своите структури от данни:ако вашите данни имат множество полета и искате да съхранявате повече от един елемент на ключ, трябва по някакъв начин да го кодирате сами. За щастие стандартната среда на Redis Lua включва поддръжка на msgpack, която е много ефективен двоичен формат, подобен на JSON. JSON записите във вашия пример, кодирани с msgpack "както е", ще бъдат дълги 52-53 байта. Предлагам да групирате по време, така че да имате 100-1000 записа на ключ. Да предположим, че интервалът от една минута отговаря на това изискване. Тогава схемата за въвеждане ще бъде следната:
YYmmddHHMMSS
— хеш от tid
до точки от данни, кодирани от msgpack за дадена минута.5m:YYmmddHHMM
, 1h:YYmmddHH
, 1d:YYmmdd
— хешове на данни на прозореца, които съдържат min
, max
, sum
полета.
Нека разгледаме примерен Lua скрипт, който ще приеме една точка от данни и ще актуализира всички ключове, ако е необходимо. Поради начина, по който работи скриптовете на Redis, трябва изрично да предадем имената на всички ключове, които ще бъдат достъпни от скрипта, т.е. живите данни и трите ключа на прозореца. Redis Lua разполага и с библиотека за разбор на JSON, така че за простота нека приемем, че просто я предадем JSON речник. Това означава, че трябва да анализираме данните два пъти:от страната на приложението и от страната на Redis, но ефектите на производителността от тях не са ясни.
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)
Тази настройка може да прави хиляди актуализации в секунда, не е много гладна за памет, а данните от прозореца могат да бъдат извлечени незабавно.
АКТУАЛИЗАЦИЯ:Относно паметта, 50-60 байта на точка са все още много, ако искате да съхранявате още няколко милиона. С този вид данни мисля, че можете да получите до 2-3 байта на точка, като използвате персонализиран двоичен формат, делта кодиране и последващо компресиране на парчета с помощта на нещо като snappy. Зависи от вашите изисквания, дали си струва да направите това.