2015-12-10 39 views
2

我正在跟蹤Redis中多個Sorted Sets中的成員,以此作爲在成員上執行多列索引的方法。舉個例子,假設我有兩個Sorted Sets,lastseen(這是紀元時間)和points,並且我在這些Sorted集中存儲用戶名作爲成員。在Redis中,是否可以對多個Sorted集合中的成員進行排序?

我被lastseen這樣我就可以得到最後一天或一個月內見過的用戶,那麼我想通過points生成的成員排序,所以我有有效的最後一天內看到的成員希望第一個排序或按月份排序的月份。

這將是容易的,如果我能儲存呼叫的結果ZREVRANGEBYSCORE到一個新的有序集合(我們稱之爲新的有序集合temp),因爲那時我可以排序lastseen有限制,結果存入temp ,使用ZINTERSTORE針對temppoints,對於out(存儲到result),權重爲0,最後在result上再次使用ZREVRANGEBYSCORE。但是,Redis沒有內置的方法將ZRANGE的結果存儲到新的Sorted Set中。

我使用here發佈的解決方案進行了研究,雖然它看起來正確地排序結果,但排序集合中的結果分數不能再用於根據時間精確限制結果(即只需要最後一天)。

例如:

redis> ZADD lastseen 12345 "foo" 
redis> ZADD lastseen 12350 "bar" 
redis> ZADD lastseen 12355 "sucka" 
redis> ZADD points 5 "foo" 
redis> ZADD points 3 "bar" 
redis> ZADD points 9 "sucka" 

我想什麼直到結束,假設我的時間窗口是1234912356之間,會員['sucka', 'bar']名單。

+0

我沒有在Redis中存儲歷史數據。例如,我不是每次都看到用戶時,就是最後一次。這意味着,每次成員登錄時,我都會更新lastseen Sorted集,其中用戶名爲成員,當前紀元時間爲分數。 – Bryan

+0

我的意思是,我沒有在Redis中存儲時間序列數據,只是最近的一次。是的,Sorted Sets在啓動時從PostgreSQL中填充,但我寧願查詢Redis而不是實時查詢PostgreSQL。無論如何,它並沒有真正改變我的問題 - 我可以輕鬆地使用一個呈現相同用例的不同示例。 – Bryan

+0

我已經在OP中有。 – Bryan

回答

1

的解決方案我能想到的是:

1)您的願望是ZREVRANGEBYSCORE,並以某種方式保存臨時結果。相反,您可以複製zset(可以使用只有一組作爲參數的ZINTERSTORE),然後在新副本上執行ZREMRANGEBYSCORE以擺脫您不感興趣的時間,然後執行最後的ZINTERSTORE

2)按照Eli的建議,在客戶端進行循環。

3)在Lua腳本中做同樣的事情。

這些都是潛在的昂貴的操作,所以最好的工作將取決於您的數據和用例。不知道更多,我會親自傾向於Lua解決方案。

+0

關於1)除非我錯過了某些東西,否則複製ZINTERSTORE並不會有幫助,因爲副本上的ZREVRANGEBYSCORE仍然不會以我可以用於最終ZINTERSTORE的方式給出結果。關於2)對於我的用例,我必須將得分加權1E10,然後將結果與最後得分相加,以便正確定購,這實際上會導致分數超出限制,因爲時間限制。 – Bryan

+0

@Bryan:1)'REM',而不是'REV'。您正在*刪除所有不在您所需時間範圍內的副本。 2)我現在看到。你連接的問題是做了一種不同的類型,第二列打破了關係。而你只是想把時間用作過濾器,而完全依靠點來完成。所以這種方法是行不通的。 –

+0

Oh dang,我不明白我是如何錯過ZREM *命令甚至存在的事實!我認爲這是你的一個錯字。我明天會試一試,然後回到這裏。 – Bryan

1

對於使這個複雜的查詢,你想用另一種處理語言來補充Redis的內置命令。最簡單的方法是從你的後端語言中調用並使用它來處理。使用redis-py Python中的一個例子是:

import redis 
finish_time, start_time = 12356, 12349 
r = redis.Redis(host='localhost', port=6379, db=0, password='some_pass') 
entries_in_time_frame = r.zrevrangebyscore('lastseen', finish_time, start_time) 
p = r.pipeline() 
for entry in entries_in_time_frame: 
    p.zscore('points', entry) 
scores = zip(entries_in_time_frame, p.execute()) 
sorted_entries = [tup[0] for tup in sorted(scores, key=lambda tup: tup[1])] 


>>> ['sucka', 'bar'] 

注意管道,所以我們永遠只能發送兩次調用Redis的服務器,所以網絡延遲不應該放慢我們的腳步了。如果你需要走得更快(也許如果第一個ZREVRANGEBYSCORE返回的內容很長),你可以重寫與上述as a Lua script相同的邏輯。這裏有一個工作示例(請注意我的Lua是生鏽,所以這可以被優化):

local start_time = ARGV[1] 
local finish_time = ARGV[2] 
local entries_in_time_frame = redis.call('ZREVRANGEBYSCORE', KEYS[1], finish_time, start_time) 

local sort_function = function (k0, k1) 
    local s0 = redis.call('ZSCORE', KEYS[2], k0) 
    local s1 = redis.call('ZSCORE', KEYS[2], k1) 
    return (s0 > s1) 
end 

table.sort(entries_in_time_frame, sort_function) 

return entries_in_time_frame 

你可以把它像這樣:

redis-cli -a some_pass EVAL "$(cat script.lua)" 2 lastseen points 12349 12356 

返回:

1) "bar" 
2) "foo" 
+0

這就是我害怕的......不是問題,只是好奇它是否可以直接在Redis中完成。 – Bryan

+0

@Bryan分享爲什麼你更喜歡lua腳本? – Eli

+0

我不一定會比Lua腳本更喜歡它,只是好奇,如果它可以沒有。我說「害怕」有點誤導。 – Bryan

相關問題