java - 如何使用redis排序集实现竞赛排行榜
问题描述
我正在使用 Redis 排序集来维护我的游戏的排行榜。我有一个场景,我需要保持与竞赛排行榜得分相同的用户的相同排名。例如。
| member | score | rank |
— — — — — — — — — — —
| member_1 | 50 | 1 |
| member_2 | 50 | 1 |
| member_3 | 30 | 3 |
| member_4 | 30 | 3 |
| member_5 | 10 | 5 |
到目前为止,我正在使用 Redis 排序集的默认实现,它按字典顺序返回排名。
127.0.0.1:6379> zadd test-leaderboard 9 user1
(integer) 1
127.0.0.1:6379> zadd test-leaderboard 5 user2
(integer) 1
127.0.0.1:6379> zadd test-leaderboard 5 user3
(integer) 1
127.0.0.1:6379> zadd test-leaderboard 3 user4
(integer) 1
如果我查询 user2 和 user3 排名,我会得到不同的结果
127.0.0.1:6379> zrank test-leaderboard user2
(integer) 1
127.0.0.1:6379> zrank test-leaderboard user3
(integer) 2
我检查了 Redis 文档,没有这样的功能可以这样做。所以我想知道我必须做什么或者实现这个功能的最佳方式是什么。
注意:我在 SET 中有 10K 条记录,我需要在运行时维护它,并且我使用的是 Java 编程语言。
解决方案
排序集首先按分数排序,然后按字典顺序排序,这就是为什么你会得到不同排名的原因user2
和user3
。
您可以结合ZSCORE
,ZRANGEBYSCORE
和ZRANK
来规范化它。基本上,您获得 的分数user3
,然后按字典顺序获得并列的第一个用户,并获得该用户的排名。
> ZSCORE test-leaderboard user3
"5"
> ZRANGEBYSCORE test-leaderboard 5 5 LIMIT 0 1
1) "user2"
> ZRANK test-leaderboard user2
(integer) 1
这会给你一个排名,平局排名相同,但排名差距。
user4 => 0
user2 => 1
user3 => 1
user1 => 3
如果您希望您的排名没有差距,您可以使用每个条目给定分数的用户列表来维护排行榜 ( ZADD test-leaderboard 5 "user2,user3"
),或者维护一个仅具有唯一分数的单独排序集。为了效率,我会选择第二个。
添加新玩家[O(log(N))]
:
ZADD test-leaderboard 5 user2
ZADD test-ranks 5 5
删除播放器[O(log(N))]
:
> ZSCORE test-leaderboard user2
"5"
> ZREM test-leaderboard user2
(integer) 1
> ZRANGEBYSCORE test-leaderboard 5 5 LIMIT 0 1
1) "anotherUser" or (empty list or set)
if(empty set)
> ZREM test-ranks 5
更新玩家分数[O(log(N))]
:
> ZSCORE test-leaderboard user2
"5"
> ZADD test-leaderboard 10 user2
(integer) 1
> ZADD test-ranks 10 10
(integer) 1
> ZRANGEBYSCORE test-leaderboard 5 5 LIMIT 0 1
1) "anotherUser" or (empty list or set)
if(empty set)
> ZREM test-ranks 5
获取玩家的排名[O(log(N))]
:
> ZSCORE test-leaderboard user2
"5"
> ZRANK test-ranks 5
(integer) 1
几点注意事项:
如果得分较高,则使用ZREVXXX
命令。
ZRANK
将最低分排在第一位,ZREVRANK
如果您希望最高分排在第一位,请使用。见ZREVRANK
和ZREVRANGEBYSCORE
。
使用 Lua 脚本
使用Lua 脚本,您可以使您的操作原子化并更快地执行它们。
这里举个例子。代替
> ZSCORE test-leaderboard user2
"5"
> ZRANK test-ranks 5
(integer) 1
使用脚本:
local score = redis.call('ZSCORE', KEYS[1], ARGV[1])
return redis.call('ZRANK', KEYS[2], score)
用于:
> EVAL "local score = redis.call('ZSCORE', KEYS[1], ARGV[1]) \n return redis.call('ZRANK', KEYS[2], score)" 2 test-leaderboard test-ranks user2
(integer) 1
推荐阅读
- python - 为什么在 for 循环中删除列表项无法正常工作?
- memcached - 有人可以解释一下 memcached 中的平板是什么吗
- sql-server - 我正在使用终端使用 VS Code 从现有数据库 .Net Core 3.0 生成模型,但它不起作用
- r - R sink() 消息并输出到同一个文件 - 完整性检查
- angular - 将清晰度数据网格的行滚动到顶部并以编程方式扩展它的更好方法是什么?
- c - fscanf 从头开始读取
- java - 排序和过滤
- ios - 具有典型菜单栏样式导航的应用程序是如何编码的?
- django - Django 显示选择值
- swift - 如何在不重新启动页面的情况下更新导航控制器上一页的信息?