Skip to content

如何使用 Redis 快速实现排行榜? ​

  • 标签:后端、Redis

🏛 业务架构师 ​

排行榜用 ZSet(有序集合):ZADD rank:{activity} score member 更新分数,ZREVRANGE 取 TopN、ZREVRANK 查自己名次,ZRANGEBYSCORE 取分数段,复杂度都在 O(logN)。十万级参赛者的榜单毫秒出结果。

业务落地我踩过的坑比 API 更值得讲:

  • 同分排序规则要先和运营对齐:ZSet 同分默认按成员字典序。而业务上"同分先到先得"要把时间戳编进 score(如 score = 分数 × 10^10 + (到期时间戳的补数)),上线后再改规则等于榜单重算,很伤——规则要在需求评审时钉死;
  • 按时间维度拆 key:rank:202610 月榜独立,自然淘汰;全站总榜量大时只保留 TopN + 懒加载,key 的生命周期设计就是存储成本设计;
  • 只存 ID 不存详情:榜单存 userId,进详情页批量查用户服务,避免冗余和更新风暴;
  • 更新走异步:点赞、消费等行为事件进 MQ,消费者聚合更新 ZSet,抗住写高峰(大促打赏榜瞬时 QPS 上万,同步写会打穿);
  • 防刷与降级:刷分识别(频次突增)与"榜单服务挂了返回缓存的上一版快照"的降级预案。

场景延展:销量榜支撑营销页、积分榜驱动会员运营、直播打赏榜是收入核心——排行榜本质是"运营活动的实时仪表盘",技术上它是小需求,业务上它常常是流量入口,稳定性和规则准确性都要按核心链路对待。

🏗 系统架构师 ​

排行榜用 Redis **ZSet(有序集合)**实现是标准答案,核心是它按 score 排序且提供排名查询:

核心操作:

  • 加分/更新:ZINCRBY rank:202610 1 user:1001(O(logN));
  • 查排名:ZREVRANK rank:202610 user:1001(第几名)、ZSCORE 查分数;
  • Top N:ZREVRANGE rank:202610 0 9 WITHSCORES;
  • 分页拉取:ZREVRANGE 带 offset/count;
  • 同分排序:score 设计成组合分(如 分数*1e10 + (MAX_TS - 时间戳)),先比分数再比早达到时间,一个字段解决多级排序。

架构权衡(生产必聊):

  • 量级:单 key 百万成员没问题,亿级要按周期分片(月榜分 30 个日榜)或按分数段拆 key,聚合用 UNIONSTORE;
  • 实时性:写操作先更新 Redis 再异步落库(消息队列削峰),Redis 是唯一读源,DB 只做持久化兜底;
  • 冷热:历史榜单转压缩表归档,活跃榜常驻;
  • 防刷:加分入口限流 + 幂等(同一行为只计一次,用 Set 或 DB 唯一约束去重)。

整个方案的本质是:把"排名"这个读多写多的计算,交给内存中 O(logN) 的有序结构,DB 退化为持久层。

💻 全栈开发专家 ​

ZSET(有序集合)一把梭,member 存用户/对象 id,score 存分值:

bash
ZINCRBY rank:activity:1001 5 "user:42"       # 加分
ZREVRANGE rank:activity:1001 0 9 WITHSCORES  # Top 10(降序)
ZREVRANK rank:activity:1001 "user:42"        # 某人的名次(0 起)
ZCARD rank:activity:1001                     # 总参与人数
java
// Spring 侧封装
boundZSetOps("rank:" + activityId).incrementScore(userId, score);
Set<String> top = boundZSetOps.reverseRange(0, 9);
Long rank = boundZSetOps.reverseRank(userId);

两个必须处理的工程细节:

  1. 同分排序规则(产品常要求"同分先到者排前"):score 是 double,我把分数和时间揉进去:score = 分数 * 1e10 + (MAX_TS - 时间戳),分数占高位、时间反序占低位,一条 score 同时表达两个排序维度。
  2. 大榜性能:ZSET 全量在一个 key,几十万 member 时 ZRANGE 也没问题,但实时分页拉全量会卡;百万级用户的活动我会分榜合并(按 userId 取模拆 100 个子榜,查询时各取 Top N 归并),或者只维护 Top 100 的精确榜 + 以下名次近似。

数据可靠性:排行榜是可重算数据,丢失后从 DB 日志重放即可,所以我会把 ZSET 操作记流水表,定时对账重建。