← 每日面试题 学习看板

双十一商品销售排行场景下如何用Redis实现排行榜功能

面试题/真实面试/2026/双十一商品销售排行场景下如何用Redis实现排行榜功能.md

双十一商品销售排行场景下如何用Redis实现排行榜功能

首次来源:心动方糖 2026-03-13 一面

原题

双十一商品销售排行场景下,如何用Redis实现排行榜功能?

完整讲解(权威版)

最后更新:2026-10-29 · 基于第 1 次作答

L1 · 30 秒速记

结论:排行榜用 Redis zset(score=销量,member=商品ID),实时订单 ZINCRBY 加分,ZREVRANGE 取 TopN;大促拆榜 + 定期与 DB 对账兜底。 关键词:zset · ZINCRBY · ZREVRANGE · 热 Key · 对账 边界:单 zset 热 Key 会打满单分片,需按 商品ID 哈希拆榜再合并;榜单必须与 DB 周期对账,否则误差累积。

L2 · 核心讲解

核心考点定位:面试官想看你对 zset 排行榜的工程落地——不只是"用 zset",还要能讲清热 Key 拆分、TopN 查询、分数变更跨段、与 DB 对账这四个大促专属难题。

机制链(≥3 环):

  1. 下单成功 → ZINCRBY rank 销量商品ID:为什么,因为 zset 按 score 有序,销量累加用 ZINCRBY 原子加。
  2. 实时加分 → 分数变化:为什么,因为 score 变更即调整 member 在跳表中的位置,ZREVRANGE 可 O(logN+M) 取 TopN。
  3. 大促单 key → 热 Key 打满单分片:为什么,因为所有写都集中到一个 zset key,Redis 单线程+单分片成瓶颈。
  4. 榜单漂移 → DB 对账:为什么,因为内存榜单可能因丢失/重复/回滚漂移,需周期用 DB 聚合重算校准。
  5. TopN 展示 → 只取头部:为什么,因为百万级商品拉全榜成本高,ZREVRANGE 0 N-1 沿跳表只取 TopN。

证据锚点(去重 ≥12):

对比与权衡:

方案实时性热 Key 风险成本适用
单 zset 实时加分高高(单 key)低中小规模
多分片 zset + 合并高低中大促、百万商品
跑批预聚合 zset低(分钟级)低低非实时排行榜
DB 直接排名差无高数据量小
Redis 缓存 DB 榜单中低中读多写少

常见误区:

  1. 错误:排行榜直接 ZRANGE 0 -1 拉全量 → 正确:只取 TopN(ZREVRANGE 0 N-1),全量拉取在百万级是灾难 → 为什么错:忽略 zset 全量查询成本。
  2. 错误:大促单 zset 就够 → 正确:单 key 写集中成热 Key,需按 商品ID 哈希拆榜再 ZUNIONSTORE 合并 → 为什么错:忽略单分片吞吐上限。
  3. 错误:榜单不用对账 → 正确:内存榜单会漂移(丢失/重复/回滚),需周期用 DB 聚合重算校准 → 为什么错:把缓存当权威数据源。
  4. 错误:score 直接用销量累计即可 → 正确:需考虑销量波动与"分数变更跨段",跨段时榜单跳变要处理 → 为什么错:忽略增量更新下的排名突变。
  5. 错误:商品 ID 直接作 member → 正确:member 应紧凑(如商品短码),避免大 key 内存膨胀 → 为什么错:忽略 zset 内存模型。

适用边界:实时性要求不高(分钟级)就跑批预聚合,用 DB 算好写入 zset 定期刷新,避免实时加分的对账复杂度;若必须实时,用多分片 zset + 周期 ZUNIONSTORE 合并 + DB 对账;榜单展示只用 TopN,永远不要 ZRANGE 0 -1 全量。若商品数极大或榜单只做展示(非强实时),可退化为「DB 聚合 + Redis 缓存榜单」的读多写少模型,降低实时加分的复杂度。

延伸追问(≥3)

  1. zset 底层结构为什么支持范围查询?
  1. 热 Key 怎么拆?拆完怎么合并?
  1. 分数变更导致排名跨段怎么处理?
  1. 如何与 DB 对账?

面试者参考回答(500–800 字)

【结论】 排行榜用 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 全量拉取;爆品跨段跃升要做好防抖或定时快照,避免榜单瞬时抖动。

面试记录 2026-10-29(第 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) 重算校准。

完整讲解见上方「完整讲解(权威版)」