我是码哥,《Redis 高手心法》作者,可以叫我靓仔。
面试时被问到"如何设计排行榜系统",可别以为是考简单的排序算法。
一旦场景升级到用户量破亿、每秒要处理 10 万次排名查询,同时数据还得保持分钟级更新时,你会发现这道题藏着分布式系统设计的几乎所有核心挑战。
今天牛哥就从需求拆解开始,一步步推导出能支撑亿级用户的排行榜架构。
明确需求
设计前不明确核心需求,就像航海没有指南针。亿级用户场景下,这四个指标直接决定系统成败,每个都要给出量化标准。

1. 实时性是关键
实时性怎么定义才合理?实测数据告诉我们:
这里的关键是用户感知:哪怕数据是异步更新的,也要通过前端动效让用户觉得实时生效。
2. 准确性是底线
榜单的核心价值在于传递可信信息,要做到「最终一致 + 不丢数据」,需要满足以下要求:

3. 抗压力决定系统能否"活下来"
双11零点的销量榜、游戏版本更新后的战力榜,峰值QPS可能从日常的 1 万飙升到100万。这时候不仅要扛住,还要保证P99 延迟 < 200ms,一旦超过这个阈值,用户会明显感觉"卡顿"。
4. 灵活性关系到业务迭代
业务方经常提需求 "今天要日榜,明天加个周榜,后天还得支持按「销量+好评率」混合排序" 。如果每次调整都要改代码、重启服务,技术团队会被业务拖着走,
所以设计时要预留「排序规则动态配置」能力。比如用JSON配置权重因子,无需发版就能生效。
{"like":2,"comment":3,"share":5}技术选型:"扛亿级"的工具组合
需求明确后,下一步是选对工具。很多同学一上来就说"用Redis ZSet",但真实场景的选型要复杂得多。这三个核心工具的组合,直接决定系统的性能天花板。
Redis ZSet:为实时榜单而生
Redis的 ZSet 为什么是实时排行榜的首选?看三个核心特性:
这里我们想知道,为什么不用数据库或搜索引擎呢?
看实际表现就很清楚:MySQL的ORDER BY在百万级数据时就会卡顿;Elasticsearch虽然支持排序,但写入延迟和资源消耗远高于 Redis。

对于90%的实时榜单场景,ZSet的性价比无人能敌。
但ZSet有个致命缺点:单Key存储上限。
Redis 单 Key 的存储大小建议控制在 100MB 以内。但在实际场景中,如果每个用户的榜单数据约为 16字节,1 亿用户的 ZSet 总大小就约为 1.6GB,远超最佳阈值 ,会导致持久化慢、主从同步延迟等性能问题。
所以亿级场景必须配合 Redis Cluster 分片,将大Key拆分成多个小 Key, 存储在不同节点,每个分片只存100万用户,大小约16MB,符合Redis最佳实践。

定时任务+分布式计算:非实时榜单的最优解
不是所有榜单都需要实时更新。日销量榜、周热门榜这类"周期结算型"榜单,用定时任务预计算比实时计算节省 90% 资源。但亿级场景下,普通定时任务框架不够用,需要分布式计算引擎配合。
XXL-Job + Spark/Flink 怎么选?看数据量和计算复杂度:

实际项目中建议分层计算:核心日榜采用 XXL-Job,因其对实时性要求较高;历史月榜选用 Spark 批处理,依托凌晨时段进行计算,能有效降低资源成本。
多存储体系:冷热数据的分层存储
Redis适合存热数据,比如实时榜、近7天榜单,但历史数据如用户历史排名记录,需要长期存储,这时候要构建多级存储体系,降低成本。
数据类型 | 存储介质 | 存储周期 | 访问延迟 | 月成本(1亿条数据) |
|---|---|---|---|---|
热数据 | Redis Cluster | 7天 | <1ms | 约5万元(100GB内存) |
温数据 | MySQL分表 | 3个月 | <100ms | 约5000元(100GB SSD) |
冷数据 | ClickHouse/Hive | 永久 | <1s | 约500元(1TB HDD) |
架构拆解:四层核心逻辑
工具选好了,接下来是搭架构,一个能抗住亿级用户的系统,一定是职责分明的。从用户行为产生到最终展示,排行榜系统可拆成 "数据接入层 - 计算排序层 - 存储层 - 展示层" 四层架构:

每层专注解决一类问题,这样即使流量翻10倍,也能通过分层扩容扛住。
数据接入层:消息队列 + 多活部署
用户的每次点击、购买、点赞,都是榜单数据的源头。亿级场景下,数据接入层的目标是「不丢数据、低延迟、高可用」,单Kafka集群不够用,需要多活部署 + 异地容灾。
比如电商平台的「全国商品销量榜」,用户广泛分布于华北、华东、华南三大核心区域,需在这三个地域分别部署 Kafka 集群,各区域用户行为数据直接写入本地 Kafka 集群;然后通过 Kafka MirrorMaker 工具,将各区域的商品销量数据跨地域同步;

数据接入层消费到 Kafka 中的行为消息后,按照特定规则计算出商品销量的实时变动。最后调用榜单系统的分数更新接口,实现榜单动态刷新。
为什么必须多活?设想一下:若仅依赖华北地区的单 Kafka 集群,一旦集群故障,华北用户的下单、加购数据将无法接入,直接导致「全国商品销量榜」 缺失华北区域数据。

而多活部署能确保任一地域集群出现问题时,其他地域集群仍能正常工作,RTO(恢复时间目标)可控制在 5 分钟内。
此外,电商数据接入层还需做好两大关键保障:
计算排序层:按场景选择架构
计算层是排行榜的"大脑",负责把原始分数转化为有序排名。亿级场景下,没有万能方案,只有按场景选择的混合架构。
实时计算:基于 Lua 脚本的即时计算
适合游戏战力榜、直播人气榜这类更新频率在秒级、数据量中等(千万级)的场景。当用户产生行为后,系统执行的命令示例如下:
1. 当用户产生行为后,通过 Lua 脚本直接操作 ZSet 完成原子化更新:
-- 原子化更新分数并返回最新排名(降序排名,战力高排第1)
local newScore = redis.call('ZINCRBY', 'game_power_ranking:server1', 50, 'user:10086')
local rank = redis.call('ZREVRANK', 'game_power_ranking:server1', 'user:10086')
return {newScore, rank}2. 同步更新本地缓存 Caffeine(Java 示例):
// 使用Caffeine更新本地缓存(用户排名与分数)
caffeineCache.put("user:10086:rank", rank);
caffeineCache.put("user:10086:score", newScore);通过这种方式,既保证了 Redis 集群中分数更新的原子性,又通过 Caffeine 本地缓存提升了后续查询效率,让排名变化能实时反馈给用户。
批量处理:Spark + ClickHouse
适合内容热度周榜、电商月销量榜这类场景。更新频率不高,通常是小时级或天级,数据量却能达到亿级规模。
每天凌晨3点,Spark 从 Kafka 消费全量行为数据,结合用户画像、时间衰减因子等维度算出最终分数,之后批量写入 ClickHouse,再同步到 Redis ZSet 中供查询。

混合计算:Flink 实时处理 + 批处理
适合「实时+历史」双维度的榜单,比如综合热度榜,既关注当下的实时热度,也参考 7 天内的累计表现,其运作流程为:
用户刚产生的点赞、评论等即时行为,会由 Flink 实时捕捉并计算出对应的实时分;到了每天凌晨,Spark 会启动批处理任务专门核算历史分,例如 7 天前的互动数据影响力会减弱,权重调整为 0.5;

之后按照 “实时分占 70%、历史分占 30%” 的公式算出综合分,比如某内容实时分 80 分、历史分 60 分,综合分就是
80(实时分) × 0.7 + 60(历史分) × 0.3 = 74 分;
而 0.7 和 0.3 这样的权重比例会预先存在 Redis 的 Hash 结构中,方便灵活调整,最终的综合分会写入 Redis ZSet,由它完成排序,既保证实时性又兼顾历史数据的影响。
存储层:Redis Cluster + 多级缓存 + 冷热分离
存储层是排行榜的"数据底座",在亿级用户的榜单场景下,存储层需要同时满足查询快和成本省的需求,核心靠 Redis Cluster 分片、多级缓存、冷热分离 这三大策略搭配实现。
Redis Cluster分片:分而治之
面对亿级用户的榜单数据,我们按用户 ID 做哈希分片:


后续如果数据量增长,只需直接增加 Redis 节点,系统会自动迁移分片到新节点,轻松实现 “横向扩容”。
多级缓存:让查询层层加速
大多数用户查榜单,只看热门内容或自己的排名,没必要每次都查全量数据。所以我们用 「三级缓存」分层承载查询需求:

冷热分离:给 Redis 减负
Redis 用内存存储,速度快但成本高,而超过 7 天的榜单数据,用户查询频率会大幅下降。所以我们每周日凌晨做一次 “冷热数据搬家”。

将超过7天的实时榜数据从Redis Cluster 同步到 ClickHouse,同步完成后删除 Redis 中的历史数据,只留下索引方便后续快速定位。
迁移过程用「双写一致性」保证:先写ClickHouse,成功后再删Redis,避免数据丢失。
展示层:CDN + API 网关 + 应用集群
展示层直接面对用户请求,核心目标是做到「毫秒级响应、全球低延迟、扛住高并发」。亿级场景下,单应用集群不够用,需要CDN加速、API网关限流、多地域应用集群三者配合。
CDN 加速静态榜单
针对首页 TOP20 这类访问频率极高的榜单,我们会先把数据生成静态 JSON 文件,再通过 CDN 分发到全球各地的节点。这样一来,不管用户在哪个地区,都能从离自己最近的 CDN 节点获取榜单数据,延迟控制在 50 毫秒以内,打开页面几乎秒加载。

为了保证数据不过时,我们给 CDN 缓存设置了 1 分钟的有效期,同时借助 API 网关的主动刷新(PURGE)机制,只要榜单数据更新,就能立刻触发 CDN 节点缓存同步,既兼顾了速度,又能让用户看到最新排名。
API 网关动态路由
全球用户的查榜请求,会先汇总到 API 网关。它主要做两件事:

应用集群弹性扩容
应用集群基于 K8s 部署,搭配 HPA 机制,也就是水平 Pod 自动扩缩容,根据实际流量自动调整资源。
比如把 CPU 利用率 70% 设为阈值:当超过 70% 时,比如晚间 8-10 点用户查榜的高峰时段,集群会自动增加 Pod 数量,最多能扩展到 100 个;而当流量回落,CPU 利用率降低时,又会自动缩减 Pod,最低保留 8 个。

这样既保证了高峰期能扛住压力,又避免了低峰期的资源浪费。
关键实现:亿级场景的避坑指南
基础架构搭好后,系统可能能用,但未必扛得住亿级流量。这些关键实现细节,决定了系统从及格到优秀的差距。
跨分片查询:从慢合并到预计算加速
查询全服 TOP100 榜单时,需要从100个分片中各查 TOP100,得到100×100=10000 个候选结果后,再合并排序取 TOP100。
亿级场景下,这个过程异常耗时,显然无法满足用户对"秒开"的需求,需要优化。
1. 预计算候选集
每个分片每 5 分钟预计算 TOP1000 的榜单数据,把这些数据缓存到本地,合并时从每个分片取 TOP1000,得到100×1000=100000 个候选结果。

虽然数据量增加10倍,但能避免"分片内 TOP100 之外的用户,实际可能是全局 TOP100" 的情况。
2. 分布式合并计算
在应用层,我们用 Java 的 PriorityQueue 也就是小顶堆来合并候选结果,把堆的大小固定为 100。遍历所有候选数据时,只要当前用户的分数高于堆顶分数,就替换堆顶元素,最终堆里剩下的就是全局 TOP100。
不过,100000 条数据的合并耗时约 30ms,再加上 100 次 Redis 查询(每次约 5ms),总耗时会达到 530ms,不满足 P99 延迟 ≤200ms 的主流标准,依然需要优化。
3. 终极优化 — 分层合并
终极解决方案是「分层合并」:先把 100 个分片按机架或可用区,分成 10 个组,每组包含 10 个分片。

第一步先在组内合并,每个组算出自己的 TOP1000;第二步再合并 10 个组的 TOP1000,得到最终的全局 TOP100。

组内合并可以直接在 Redis Proxy 层完成,这样应用层只需发起 10 次组查询,再做一次全局合并即可。优化后总耗时降到 80ms(10 次查询 ×5ms + 合并 30ms),完全满足亿级场景的响应要求。
数据一致性:从最终一致到可追溯
亿级场景下,绝对一致性无法实现,因为跨分片实时同步成本太高,但要保证「最终一致+可追溯」。具体通过三层策略实现:
实时数据一致性
主要通过保证单分片原子性,跨分片定期修复的策略实现:

历史数据一致性
主要通过「批处理对账 + 告警排查」实现。每天用 Spark 批处理计算用户的每日总分,再和 Redis Cluster 中存储的分数总和对比,允许误差控制在 0.1% 以内;如果超过这个阈值,就会触发告警,提醒工程师排查原因。
用户行为可追溯
每一次分数更新操作,都会记录详细日志,包括用户 ID、行为类型、分数增减量、操作时间戳、请求 ID 等。日志通过ELK存储,支持按用户ID/时间范围查询,当用户投诉分数异常时,工程师能通过日志快速定位问题根源。

监控告警体系:亿级下的可观测性
亿级系统"黑盒运行"等于裸奔,必须构建完善的监控告警体系,覆盖分片健康度、数据一致性、性能指标。
1. 分片健康度监控
监控每个 Redis 分片的QPS、内存使用率、响应时间,尤其是响应时间重点关注 P99、P999 分位值。一旦任一指标触及设定的阈值,比如 QPS 超过 1 万、内存使用率超过 80%、P99 响应时间超过 100ms,就会立刻发送告警;

同时监控分片迁移状态,要是迁移速度低于 10MB/s,同样会触发告警,以此避免迁移超时影响服务的可用性 。
2. 数据一致性监控
3. 用户体验监控
通过前端埋点收集榜单加载时间,P95>500ms 时触发告警,及时发现CDN缓存失效、应用集群过载等问题。
总结:亿级排行榜的设计心法
设计亿级用户排行榜,本质是对"实时性 - 准确性 - 成本 - 可用性"的四重权衡。记住这6个核心原则,无论面试还是实战都能游刃有余:
1. 大 Key 必须分片,小 Key 优化存储:单ZSet存不下亿级用户,用Redis Cluster按哈希分片;非热门数据启用ziplist编码
2. 实时用 Redis+Lua,批处理用 Spark/Flink
3. 跨分片查询分层合并:先组内合并再全局合并
4. 多级存储控成本:热数据、温数据、冷数据分层存储
5. 数据一致性可追溯:单分片原子操作+定期对账+行为日志,保证最终一致且问题可追溯
6. 监控容灾不可少:分片健康度、数据一致性、用户体验全链路监控,确保系统活下来
最后想说,面试时被问到这类问题,别再直接说"用Redis ZSet"了,先问清楚业务场景,再给出分层方案,这才是面试官想看到的系统设计能力。


学习与交流

如果你在编程面试、技术提升的路上苦苦摸索,别错过!码哥专注聚焦高频面试场景题,深挖技术要点与解题思路,持续输出干货。
公众号定期更新系列知识,帮你精准攻克面试难关。