大部分游戏中都有排行榜的存在,既可以展示玩家分数之间的排名,也可以用于激发玩家的胜负欲。
思路
排行榜的设计思路比较粗暴,谁的分数高谁在前,分数一致谁先达成谁在前。实现方式有多种,这里列举常见的2种:
- 数据库
通过任务定时查询数据库进行排序,然后储存在内存中。优点是逻辑简单,缺点是不能实时排名。
- Redis
使用zset(有序集合)存储数据,利用score存储不同的指标来实现各种排行榜。优点是能实时查询,缺点是逻辑相对复杂。
数据库
1 | select `id`,`score` from `roles` order by score desc limit 200; -- 按照score排名,取top200 |
Redis
插入/更新数据
zAdd {key} {score float} {member string}
1 | > zAdd rank:score 60.5 pp |
查询列表
zRevRange {key} {min int} {max int}
min和max指的是数据下标,从0开始
1 | > zRevRange rank:score 0 -1 |
查询排名
zRevRank {key} {member string}
1 | > zRevRank rank:score pp |
实现逻辑
1 | func setRank(key string, roleId uint32, score float64) { |
排行榜分页
1 | func SlicePage(page, pageSize, total int) (s, e int) { |
这样也有一个可能存在的瓶颈问题,当单个排行榜的数据量过大时会影响每次的查询效率。这里可以采用跳数索引
的概念来进行优化,原理很简单。
玩家按照分数进行分key存储,即每个分数区间都有对应的key。
比如:
key | 定义 |
---|---|
rank:score:100 | 小于100分 |
rank:score:500 | 小于500分&大于100分 |
rank:score:1000 | 小于1000分&大于500分 |
rank:score:1000more | 大于1000分 |
进行分段存储后,优点是查询不同区间的排名时检索的数据量显著下降,缺点是查询逻辑比较复杂。比如查询排名,需要计算高于当前分数区间的玩家数量。
最后
2024,新的一年~祝身体健康、财源滚滚、万事顺心。