Redis zrevrangebyscore, sorting other than lexicographical order

元气小坏坏 提交于 2019-12-21 06:45:09

问题


I have implemented a leader board using sorted sets in redis. I want users with same scores to be ordered in chronological order, i.e., user who came first should be ranked higher. Currently redis supports lexicographical order. Is there a way to override that. Mobile numbers are being used as members in sorted set.

One solution that I thought of is appending timestamp in front of mobile numbers and maintaining a hash to map mobile number and timestamp.

$redis.hset('mobile_time', '1234567890', "#{Time.now.strftime('%y%m%d%H%M%S')}")
pref = $redis.hget('mobile_time, '1234567890'')
$redis.zadd('myleaderboard', "1234567890:#{pref}")

That way I can get rank for a given user at any instance by adding a prefix from hash.

Now this is not exactly what I want. This will return opposite of what I want. User who comes early will be placed below user who comes later(both with same score).

Key for user1 = 201210121953**23**01234567890    score: 400
key for user2 = 201210121253**26**09313123523    score: 400 (3 seconds later)

if I use zrevrangebyscore, user2 will be placed higher than user1.

However, there's a way to get the desired rank:

users_with_higher_score_count = $redis.zcount("mysset", "(400", "+inf")
users_with_same_score = $redis.zrangebyscore("mysset", "400", "400")

Now I have the list users_with_same_score with correct ordering. Looking at index I can calculate rank of the user.

To get leader board. I can get members in intervals of 50 and order them through ruby code. But it doesn't seems to be a good way.

I want to know if there's a better approach to do it. Or any improvements that can be made in solution I purposed.

Thanks in advance for your help.

P.S. Scores are in multiples of 50


回答1:


The score in a sorted set supports double precision floating point numbers, so possibly a better solution would be to store the redis score as highscore.timestamp

e.g. (pseudocode)

highscore = 100
timestamp = now()
redis.zadd('myleaderboard', highscore + '.' + timestamp, playerId)

This would mean that multiple players who achieved the same high score will also be sorted based on the time they achieved that high score as per the following

For player 1...

redis.zadd('myleaderboard', '100.1362345366', "Charles")

For player 2...

redis.zadd('myleaderboard', '100.1362345399', "Babbage")

See this question for more detail: Unique scoring for redis leaderboard




回答2:


The external weights feature of the sort command is your saviour here


SORT mylist BY weight_*

http://redis.io/commands/sort




回答3:


If you are displaying leaderboard in descending order of score then I don't think the above solution will work. Instead of just appending timestamp in the score you should append Long.MAX_VALUE - System.nanoTime() So your final score code should be like -

highscore = 100
timestamp = Long.MAX_VALUE - System.nanoTime();
redis.zadd('myleaderboard', highscore + '.' + timestamp, playerId);

Now you will get the correct order when you call redis.zrevrange('myleaderboard', startIndex, endIndex)



来源:https://stackoverflow.com/questions/12874181/redis-zrevrangebyscore-sorting-other-than-lexicographical-order

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!