排行榜运营细节
用有序集合做排行榜,难点不在写入,而在运营细节:同分时谁排前面、翻页怎么取、周榜月榜怎么生成。分数相同的情况下顺序由成员名决定,因此想要「先到先得」必须把时间信息编进分数;周期榜可以用按天分键再合并的方式生成。
基本信息
| 写入 | ZADD 键 分数 成员,分数相同会覆盖旧分数 |
|---|---|
| 取榜单 | ZREVRANGE 键 起始 结束 WITHSCORES,从高到低 |
| 查名次 | ZREVRANK 键 成员,复杂度与集合规模对数相关 |
| 合并榜单 | ZUNIONSTORE 支持多键相加并指定权重与聚合方式 |
| 名次起点 | 排名从 0 开始,展示时通常加一 |
典型使用场景
- 日榜与周榜:按日期分键写入,周期结束时合并成一个榜单键
- 积分排行:分数即积分,同分时需要稳定的展示顺序
- 活动榜单:活动结束前只展示前若干名,避免全量拉取
- 个人名次查询:用户查看自己在第几名,用查名次命令即可
- 榜单快照:定期把结果落到数据库,用于历史回看与对账
常用操作
常用操作
ZADD lb:day 100 u1 100 u2 90 u3
ZREVRANGE lb:day 0 2 WITHSCORES # 取前三名
ZREVRANK lb:day u3 # 查某人名次(0 起)
ZSCORE lb:day u1 # 查某人分数
ZREVRANGE lb:day 50 60 WITHSCORES # 取第 51 到 61 名(分页)
# 周榜 = 两天的日榜相加(权重都为 1)
ZUNIONSTORE lb:week 2 lb:1012 lb:1013 WEIGHTS 1 1 AGGREGATE SUM
EXPIRE lb:week 604800 # 周期榜设置过期,避免长期堆积
# 同分先到先得:把时间编进分数(分数整体左移,末尾接反向时间)
# 例如 score = 业务分 * 10000000000 + (最大时间戳 - 完成时间戳)操作与注意点
| 操作 | 用途 | 注意事项 |
|---|---|---|
| 从高到低取范围 | 取榜单前若干名 | 用错命令会得到升序结果 |
| 查名次 | 个人名次展示 | 排名从零开始,展示要加一 |
| 合并榜单 | 生成周期榜 | 权重与聚合方式要写清楚 |
| 分页取段 | 榜单翻页 | 不要全量拉取,按区间取 |
| 设过期 | 清理临时榜单 | 周期榜不设过期会长期占内存 |
同分顺序由成员名决定:需要对用户友好时,把「更早完成」编码成更大的分数尾巴,排序才是稳定的。
用浮点数直接编时间戳会丢精度:分数放大成整数运算,才能保证排序不出现错乱。