我正在使用redis来实现排行榜。我要解决的问题是 - 给予用户,在他的上方获得五个用户,在排行榜中获得五个以下用户。
以下是我采取的方法,请告诉我,如果它是最佳的,或者可以做些更好的事情:
1. lower_key = zrank('set_name', 'member_name') // get the position of the user in the set
2. higer_key = zcard('set_name') // the total no. of elements in the leaderboard
3. low = max(0, lkey-5) // edge-case if user rank is less than 5.
4. high = min(key+5, higher_key) // edge-case if user rank lies is top-5
5. zrange('set_name', low, high) // get the range between the intervals.
zrank is O(log(N))
zcard is O(1)
zrange step is O(log(N)+M)
有更好的方法来执行此操作吗?
EIDT:其中一个答案提到过多的来回切换,因此我添加了一个管道,请看一下实现 -
pipeline = self.redis_connection.pipeline()
lkey = pipeline.zrank(leaderboard_name, member)
hkey = pipeline.zcard(leaderboard_name)
inter = int(self.DEFAULT_PAGE_SIZE)/2
low = max(0, key-inter)
high = min(key+inter, hkey)
pipeline.zrange(leaderboard_name, low, high)
return pipeline.execute()
请让我知道你的想法。
答案 0 :(得分:1)
所以,你当前的方法很好并且有效(除了变量名中的拼写错误),但是在你的客户端和redis服务器之间需要大量的来回,而这通常是Redis的瓶颈结束的地方。在您的情况下来回是不必要的,因为您实际上可以在单个LUA脚本中执行所有操作,然后从客户端作为Redis命令运行。然后,一切都在Redis服务器上完成,并且在您的情况下只有一个来回而不是3个。
以下是我在LUA(未经测试)中的表现:
local key_idx = redis.call("ZRANK", KEYS[1], ARGV[1])
local card_idx = redis.call("ZCARD", KEYS[1])
local low_idx = math.max(0, key_idx-5)
local high_idx = math.min(key_idx+5, card_idx)
local return_arr = redis.call("ZRANGE", KEYS[1], low_idx, high_idx)
return return_arr
然后你可以从redis中调用它:
redis-cli eval "$(cat ./myscript.lua)" 1 sorted_set_name, member_name