Q30: 如何设计排行榜系统?
核心结论
排行榜系统真正要解决的,不是“把人按分数排一下”,而是同时处理好:
- 排名更新成本
- 查询热点
- 周期结算
- 跨服聚合
- 奖励发放和审计
大多数项目里,排行榜不是单一表结构问题,而是“实时视图加结算流程”问题。
一、先明确排行榜类型
不同排行榜,设计重点完全不同。
常见类型包括:
- 实时成长榜,例如等级、战力
- 竞技榜,例如积分、段位
- 周期活动榜,例如周榜、赛季榜
- 公会或跨服榜
需要先分清:
- 是实时展示为主,还是结算奖励为主
- 是单服,还是跨服
- 是只查 Top N,还是大量查个人名次
二、排行榜最核心的两类访问
通常离不开两种:
1. Top N 查询
例如前 10、前 100、前 1000。
2. 个人名次查询
例如“我现在第几名”“我附近的人是谁”。
这两类查询如果没有提前按访问模式设计,后期很容易变慢。
三、为什么排行榜不适合每次都扫数据库
原因很直接:
- 查询频繁
- 排序成本高
- 热点集中
所以大多数实时排行榜都会放到更适合排序和快速查询的结构里,例如:
- Redis 有序集合
- 内存排序结构
- 专门的聚合服务
数据库更适合承接持久化快照和结算结果,而不是直接扛所有实时排名查询。
四、实时榜和结算榜要分开看
1. 实时榜
更关注:
- 快速更新分数
- 快速查 Top N
- 快速查个人排名
2. 结算榜
更关注:
- 截止时间点的最终结果
- 奖励发放顺序
- 审计与复核
很多事故都来自把“实时变化中的榜单”直接当作“最终结算依据”。
更稳妥的做法通常是:
- 结算时生成冻结快照
- 奖励按冻结结果发放
五、跨服排行榜为什么更难
因为它不仅是排序问题,还涉及:
- 多服数据汇总
- 更新频率控制
- 口径一致
- 延迟可接受范围
很多跨服榜不适合每次变化都全量实时同步,常见做法是:
- 分服先维护本地榜
- 聚合层按周期或流式合并
- 客户端接受短时间延迟
六、奖励发放必须和排名解耦
排行榜展示错一两秒,通常只是体验问题;奖励发错,就是严重事故。
所以结算链路通常需要:
- 冻结榜单
- 幂等发奖
- 发奖流水
- 可补偿
不要把“客户端看到的最新榜”直接作为发奖依据。
七、工程上常见的稳妥结构
很多项目最终会是:
- Redis 或内存结构承接实时排名
- 数据库保存角色基础数据和结算快照
- 单独的结算任务负责赛季或活动发奖
- 高价值奖励链路具备幂等和审计
这样职责更清晰。
八、常见误区
1. 排行榜就是 Redis ZSet
ZSet 很常用,但它只解决了排序和查询的一部分问题,不自动解决结算、发奖、跨服聚合。
2. 排名必须绝对实时
很多场景并不需要。为了几秒内的完全实时付出高昂系统成本,往往不划算。
3. 排行榜展示和奖励发放可以共用同一条链路
风险很大。展示允许小延迟,奖励不能出错。
参考资料
- Redis Sorted Set 官方资料
- 各类在线游戏赛季榜与活动榜结算实践资料
