首次来源:心动方糖 2026-03-13 一面
双十一商品销售排行场景下,如何用Redis实现排行榜功能?
最后更新:2026-10-29 · 基于第 1 次作答
结论:排行榜用 Redis zset(score=销量,member=商品ID),实时订单ZINCRBY加分,ZREVRANGE取 TopN;大促拆榜 + 定期与 DB 对账兜底。 关键词:zset · ZINCRBY · ZREVRANGE · 热 Key · 对账 边界:单 zset 热 Key 会打满单分片,需按商品ID哈希拆榜再合并;榜单必须与 DB 周期对账,否则误差累积。
核心考点定位:面试官想看你对 zset 排行榜的工程落地——不只是"用 zset",还要能讲清热 Key 拆分、TopN 查询、分数变更跨段、与 DB 对账这四个大促专属难题。
机制链(≥3 环):
ZINCRBY rank 销量商品ID:为什么,因为 zset 按 score 有序,销量累加用 ZINCRBY 原子加。ZREVRANGE 可 O(logN+M) 取 TopN。ZREVRANGE 0 N-1 沿跳表只取 TopN。证据锚点(去重 ≥12):
ZINCRBY key increment member:原子加分,排行榜核心写入。ZREVRANGE key 0 N-1 WITHSCORES:取 TopN(按 score 降序)。ZADD/ZSCORE/ZRANK:写入/查分/查名次。zset 底层:skiplist + dict,跳表保证范围查询 O(logN)。key_#hash(member),多分片后再合并(ZUNIONSTORE)。ZUNIONSTORE dest N key...:合并多分片排行。ZREVRANGE 大 N:排行榜只取 TopN,不取全量,避免 ZRANGE 0 -1 拉全榜。TTL:榜单缓存设过期(如 1 天),配合跑批重建。SELECT product_id, SUM(qty) FROM order WHERE ... GROUP BY product_id。lua 脚本:加分+判断原子操作。ZREVRANGE key 0 N-1 WITHSCORES:只取 TopN,避免 ZRANGE 0 -1 全量。ZREMRANGEBYRANK:淘汰非 TopN 成员,控制 zset 体积。SETNX + 随机 TTL 防击穿与雪崩。对比与权衡:
| 方案 | 实时性 | 热 Key 风险 | 成本 | 适用 |
|---|---|---|---|---|
| 单 zset 实时加分 | 高 | 高(单 key) | 低 | 中小规模 |
| 多分片 zset + 合并 | 高 | 低 | 中 | 大促、百万商品 |
| 跑批预聚合 zset | 低(分钟级) | 低 | 低 | 非实时排行榜 |
| DB 直接排名 | 差 | 无 | 高 | 数据量小 |
| Redis 缓存 DB 榜单 | 中 | 低 | 中 | 读多写少 |
常见误区:
ZRANGE 0 -1 拉全量 → 正确:只取 TopN(ZREVRANGE 0 N-1),全量拉取在百万级是灾难 → 为什么错:忽略 zset 全量查询成本。商品ID 哈希拆榜再 ZUNIONSTORE 合并 → 为什么错:忽略单分片吞吐上限。score 直接用销量累计即可 → 正确:需考虑销量波动与"分数变更跨段",跨段时榜单跳变要处理 → 为什么错:忽略增量更新下的排名突变。适用边界:实时性要求不高(分钟级)就跑批预聚合,用 DB 算好写入 zset 定期刷新,避免实时加分的对账复杂度;若必须实时,用多分片 zset + 周期 ZUNIONSTORE 合并 + DB 对账;榜单展示只用 TopN,永远不要 ZRANGE 0 -1 全量。若商品数极大或榜单只做展示(非强实时),可退化为「DB 聚合 + Redis 缓存榜单」的读多写少模型,降低实时加分的复杂度。
skiplist 跳表 + dict;ZREVRANGE 沿跳表 O(logN+M) 取区间。key_{hash(member)%N} 分 N 片,查询 ZUNIONSTORE 合并;分片数按单分片容量估。GROUP BY product_id SUM(qty) 重算,DEL 后重建或增量校准。【结论】 排行榜用 Redis zset:score=销量、member=商品ID,实时订单 ZINCRBY 加分,ZREVRANGE 取 TopN;大促必须拆榜 + 定期与 DB 对账。
【三点】 第一,写入与查询:下单成功后 ZINCRBY rank 1 productId 原子累加,查询用 ZREVRANGE rank 0 9 WITHSCORES 取 Top10,zset 底层跳表让范围查询 O(logN+M),比全量排序高效。第二,大促热 Key 拆分:单 zset 所有写集中到一个 key,会打满单分片,按 商品ID 哈希 拆成多个 key_{hash},查询时 ZUNIONSTORE 合并再 ZREVRANGE。第三,可靠性对账:内存榜单可能因消息丢失、重复消费、订单回滚而漂移,必须周期用 DB GROUP BY product_id SUM(qty) 重算校准,不能把缓存当权威数据源。
【边界】 如果实时性只要分钟级,直接跑批预聚合(DB 算好写入 zset 定期刷新)最省心,不用承担实时加分的对账复杂度;榜单展示永远只取 TopN,别 ZRANGE 0 -1 全量拉取;爆品跨段跃升要做好防抖或定时快照,避免榜单瞬时抖动。
题目:双十一商品销售排行场景下如何用Redis实现排行榜功能
难度:high
标签:db/redis/advanced, real-interview, difficulty/high
评分:⭐⭐⭐⭐☆(4/5)
练习次数:1
双十一那种大规模场景下销售排行榜,按照的是产品销量去排行,做topN的数据排行,实现选择的话排行榜选择redis的zset去存储数据
把销量作为score,产品名称做为key;初始化做一轮数据统计,加载初始化的排行榜数据,看这个排行榜是否有实时需求,如果有则对于消费数据订单做分数递加,通过这个分数做排行;
如果没那么多要求,就是每日排行榜的话,计划任务跑批就好;对产品的每分钟做一个统计,每分钟/每小时的销量统计一轮做聚合防止中间的追加数据存在误差或异常的时候导致数据长期错误,周期性的刷新下排行榜
优点:选型正确(zset,score=销量,member=商品),实时加分 vs 非实时跑批聚合两种模式清晰,初始化加载 + 周期性刷新防长期误差。
不足:未提热 Key 拆分(大促单 zset 打满单分片)、ZREVRANGE 只取 TopN、与 DB 对账的具体手段。
补充:大促按 商品ID 哈希拆榜 + ZUNIONSTORE 合并;榜单展示只取 TopN;跑批 GROUP BY SUM(qty) 重算校准。
完整讲解见上方「完整讲解(权威版)」