Mein Vorschlag ist, min/max/total für alle Intervalle zu speichern, an denen Sie interessiert sind, und sie mit jedem ankommenden Datenpunkt für aktuelle zu aktualisieren. Um Netzwerklatenz beim Lesen früherer Daten zum Vergleich zu vermeiden, können Sie dies mithilfe von Lua-Skripten vollständig innerhalb des Redis-Servers tun.
Ein Schlüssel pro Datenpunkt (oder, noch schlimmer, pro Datenpunktfeld) verbraucht zu viel Speicher. Für die besten Ergebnisse sollten Sie es in kleine Listen/Hashes gruppieren (siehe http://redis.io/topics/memory-optimization). Redis erlaubt nur eine Verschachtelungsebene in seinen Datenstrukturen:Wenn Ihre Daten mehrere Felder haben und Sie mehr als ein Element pro Schlüssel speichern möchten, müssen Sie es irgendwie selbst codieren. Glücklicherweise enthält die standardmäßige Redis Lua-Umgebung msgpack-Unterstützung, die ein sehr effizientes binäres JSON-ähnliches Format ist. JSON-Einträge in Ihrem Beispiel, die mit msgpack "as is" codiert sind, sind 52-53 Byte lang. Ich schlage vor, nach Zeit zu gruppieren, sodass Sie 100-1000 Einträge pro Schlüssel haben. Angenommen, ein einminütiges Intervall erfüllt diese Anforderung. Dann würde das Schlüsselschema wie folgt aussehen:
YYmmddHHMMSS
— ein Hash von tid
zu msgpack-codierten Datenpunkten für die angegebene Minute.5m:YYmmddHHMM
, 1h:YYmmddHH
, 1d:YYmmdd
— Fensterdaten-Hashes, die min
enthalten , max
, sum
Felder.
Schauen wir uns ein Lua-Beispielskript an, das einen Datenpunkt akzeptiert und alle Schlüssel nach Bedarf aktualisiert. Aufgrund der Funktionsweise von Redis-Skripten müssen wir explizit die Namen aller Schlüssel übergeben, auf die das Skript zugreift, d. h. die Live-Daten und alle drei Fensterschlüssel. Redis Lua hat auch eine JSON-Parsing-Bibliothek zur Verfügung, also gehen wir der Einfachheit halber davon aus, dass wir ihr einfach das JSON-Wörterbuch übergeben. Das bedeutet, dass wir Daten zweimal parsen müssen:auf der Anwendungsseite und auf der Redis-Seite, aber die Auswirkungen auf die Leistung sind nicht klar.
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)
Dieses Setup kann Tausende von Aktualisierungen pro Sekunde durchführen, ist nicht sehr speicherintensiv und Fensterdaten können sofort abgerufen werden.
UPDATE:Beim Speicherteil sind 50-60 Bytes pro Punkt immer noch viel, wenn Sie ein paar Millionen mehr speichern möchten. Mit dieser Art von Daten können Sie meiner Meinung nach nur 2-3 Bytes pro Punkt erreichen, indem Sie ein benutzerdefiniertes Binärformat, Delta-Codierung und anschließende Komprimierung von Blöcken mit etwas wie Snappy verwenden. Ob sich das lohnt, hängt von Ihren Anforderungen ab.