我的建議是存儲您感興趣的所有區間的最小/最大/總數,並使用每個到達的數據點更新當前區間。爲了避免在讀取以前的數據進行比較時出現網絡延遲,您可以使用Lua腳本完全在Redis服務器內完成此操作。
每個數據點的一個關鍵(或者更糟糕的是,每個數據點字段)將消耗太多內存。爲了獲得最佳效果,您應該將它分組爲小列表/散列(請參閱http://redis.io/topics/memory-optimization)。 Redis只允許在其數據結構中嵌套一層:如果數據有多個字段,並且您希望每個鍵存儲多個項目,則需要以某種方式自己對其進行編碼。幸運的是,標準的Redis Lua環境包括msgpack支持,這是一種非常有效的二進制JSON格式。在您的示例中,msgpack「按原樣」編碼的JSON條目長度爲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個字節仍然很多。有了這種數據,我認爲使用自定義二進制格式,增量編碼以及隨後使用諸如snappy之類的塊壓縮,每點可以獲得低至2-3個字節的數據。這取決於你的要求,是否值得這樣做。
我想你想要3個滑動窗口(每5分鐘一個,每個小時一個,每天一個),你想每個時間段的最高/最低值,我理解正確嗎? – zenbeni
是的,有些圖表就像http://www.investing.com/equities/yahoo-inc-candstick。我想知道將原始數據存儲到數據庫的最佳做法是什麼。 – yuyue007