2

I am using redis to implement the leaderboard. The problem statement I am addressing is - given a user, get five users above him, and five users below him in the leaderboard.

Following is the approach, that I have taken, please let me know, if it is optimal, or something better can be done:

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) 

Is there a better way to perform this operation?

EIDT : One of the answer mentioned about too much of back and forth switching, hence I added a pipeline, please have a look at the implementation -

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()

Please let me know your thoughts.

4

1 回答 1

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
于 2013-05-16T21:20:09.550 回答