我正在Redis中的多个排序集中跟踪成员,作为对成员进行多列索引的方法。举个例子,假设我有两个排序集lastseen
(这是一个纪元时间)和points
,我将用户名存储为这些排序集中的成员。
我想首先按lastseen
进行排序,以便我可以在最后一天或一个月内看到用户,然后我想要按points
对结果成员进行排序,这样我才能有效地在最后一天或一个月内按成分排列的成员。
如果我可以将对ZREVRANGEBYSCORE的调用结果存储到新的排序集(我们将调用新的排序集temp
),这将很容易,因为那时我可以对lastseen
进行排序限制,将结果存储到temp
,对temp
使用ZINTERSTORE,对points
使用权重为零的out
(存储到result
),最后使用ZREVRANGEBYSCORE再次result
。但是,Redis中没有内置的方法可以将ZRANGE的结果存储到新的排序集中。
我研究过使用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"
如果我的时间窗口在12349
和12356
之间,我希望最终得到的是成员列表['sucka', 'bar']
。
答案 0 :(得分:1)
对于复杂的查询,您希望使用其他处理语言来补充Redis的内置命令。最简单的方法是从你的后端语言中调用并使用它来处理。 Python中使用redis-py的一个例子是:
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"
答案 1 :(得分:1)
我能想到的解决方案是:
1)你的愿望是ZREVRANGEBYSCORE
并以某种方式保存临时结果。相反,您可以复制zset(可以使用ZINTERSTORE
只用一个参数作为参数完成),然后在新副本上执行ZREMRANGEBYSCORE
以摆脱您不感兴趣的时间在,然后做最后的ZINTERSTORE
。
2)正如Eli建议的那样,在客户端循环执行。
3)在Lua脚本中执行相同的操作。
这些都是潜在的昂贵操作,因此最好的方法取决于您的数据和用例。如果不了解更多,我会亲自倾向于Lua解决方案。