news 2026/9/17 3:21:21

亿级排行榜架构设计:Redis ZSet分片与冷热分离实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
亿级排行榜架构设计:Redis ZSet分片与冷热分离实战

做了这么多年后端,排行榜这个需求我接过太多回了。小到几千人在线的小活动,大到上亿注册用户的全量积分榜,不同量级下的解法完全是两码事。今天想把我自己对“亿级排行榜”这套方案设计的思考完整梳理一遍,说说我从最朴素的数据库方案一路演化到最终落地的整套架构,以及每一版踩过的坑和做选择的理由。如果你正在面对类似体量的榜单需求,这篇文章应该能帮你省掉不少试错成本。

先明确一下场景:这里的“亿级”指的是参与排名的用户数达到1亿以上,而且不是那种一次性导入的静态数据,是随时有用户行为产生、分数持续变动、榜单实时性要求较高的业务场景。典型的例子像游戏全服战力榜、金融App的成长值排行、运动健康类的步数总榜。这类场景最麻烦的地方在于,不光是数据量大,写入的QPS和查询的实时性要求也都不低,单纯靠某一种存储解决方案很难完全覆盖。

这篇文章适合正在做架构选型、或者被排行榜性能问题卡住的后端同学。我会把从需求拆解、底层数据结构选型,到分片策略、排序细节、缓存降级,再到线上压测和问题排查的完整链路都过一遍。

1. 动手之前先想清楚的事:你的排行榜到底属于哪种类型

很多人一上来就开始选Redis还是数据库,这其实搞错了顺序。亿级排行榜第一个要解决的问题不是技术选型,而是需求本身的定性。不同性质的排行榜,对“亿级”这个词的含义是完全不一样的,能接受的实现成本和性能指标也天差地别。

1.1 全量精确榜、活跃榜和TopN榜,差别比想象中大得多

我习惯把排行榜粗暴地分成三类。第一类是全量精确榜,所有注册用户都参与排名,每一个用户都能查到自己的准确名次。这个最硬核,因为一亿用户的排名结果本身就是一个一亿条记录的映射关系,而且数据还在实时变化。大多数客户嘴上说要的“排行榜”,心理预期其实是这种。

第二类是活跃榜或准活跃榜,虽然号称亿级用户,但真正进入榜单只需要覆盖最近7天有过活跃行为的用户。这个对排名人数其实是打折的,可能只有千万级甚至百万级的量需要动态维护,剩余用户要么不展示名次,要么给一个“超过xx%的用户”这种粗粒度区间。这类排行榜做起来要轻松得多,很多优化空间就是从这类需求里抠出来的。

第三类是TopN榜,核心诉求就是展示前100名、前500名,个人名次可以用近似值甚至不查。排行榜在业务上最大的流量入口其实集中在头部榜单的浏览上,TopN榜可以把精确计算的范围死死压缩在一个极小集合内。

这三类需求在数据量级、更新频率、实时性要求上完全不同,方案设计的差异极大。我见过最典型的翻车案例,就是拿全量精确榜的存储方案去套TopN榜的需求,结果为了一个没人看的第99999999名,白白烧掉了几台16G内存的Redis节点。

1.2 亿级用户不等于亿级排名数据

这是做亿级排行榜时最容易忽略的一个判断。一亿用户注册,并不意味着排行榜里真的要维护一亿条记录。很多业务有准入门槛,比如游戏全服战力榜只收录30级以上的角色,比如信用卡积分榜只收录有积分的用户。真正进入可查询名次的集合,常常比总用户数少一个数量级。

所以在方案设计前,第一件事是把真实参与排名的实体数量估算出来。这个数量的量级直接决定后面的技术路线。如果是千万级以内的活跃排名者,一个设计良好的Redis ZSet集群完全兜得住;如果超过这个量级,才需要考虑分片和合并。先把这个数字估算清楚,方案就完成了一半。实际估算的时候可以问产品要数据,如果产品也给不出来,就按注册量乘以一个业务转化系数来预估,宁可多估不要少估,因为榜单数据一旦上线,增长曲线通常比你想象的要陡。

2. 为什么不能靠数据库order by,也不能只靠一个Redis ZSet

确定了需求之后,接下来就是选型。这个环节我不想直接甩结论,而是把每种方案的底层原理和瓶颈说清楚,这样你们以后遇到类似问题也能自己推导。

2.1 MySQL的排序性能瓶颈出在数据访问模式上

第一反应上数据库是很正常的事,毕竟业务数据本来就存在MySQL里,users表加个索引,order by score desc limit 100不就行了吗。在数据量小的时候确实行,但到了亿级,问题就爆发了。

首先,排序本身要命。哪怕你的score字段上有索引,MySQL对亿级数据做order by score desc limit 100这种查询,优化器也大概率不会走索引顺序扫描,因为你要的不是最大分数段里那批记录,而是明确的前100条。即使能走索引,每次查询都要从B+树的最右侧开始遍历,那没问题,但问题是只要有用户的分数一更新,索引顺序就变,MySQL需要实时维护这些更新。单个热点行的瓶颈不一定出现,但批量导入、高并发加分时,索引维护的开销会很明显地拖慢写入速度。

更致命的是随机IO。一张一亿行的表,行宽度随便几十个字节,光表数据就好几个GB,加上二级索引就更多了。要取前100名,如果这些用户刚好分布在不同的数据页上,你就得反复回表,每一次回表都可能触发磁盘IO。这个性能特征极其不稳定,在缓存命中率高的时候快如闪电,一旦缓存被冲掉就瞬间雪崩。在线上环境里,这种不确定性是灾难性的。

数据库在这类场景里不是不能用,但必须做分层。离线场景比如每日结算一次的名次、每周更新的积分榜,用MySQL的只读从库做预计算没问题。但只要涉及在线实时更新和实时查询,数据库就不该充当核心存储了。它在亿级排名场景里的角色,应该退回到“数据源”而不是“排名引擎”。

2.2 Redis ZSet很香,但单key的承载能力有明确天花板

Redis自带的ZSet(有序集合)几乎是排行榜场景里最顺手的原生数据结构,底层是跳表加哈希表的组合。跳表保证插入、删除、按分数查询排名的操作都在O(log n)完成,哈希表用来做member到分数的映射和去重。这个结构用起来是真的舒服,一个ZINCRBY把用户分数加进去,一个ZREVRANK查到名次,ZRANGE取出榜单片段,一套流程下来代码不超过10行。

但ZSet最隐蔽的坑在内存上。Redis所有数据都在内存里,一个亿级成员的ZSet,内存开销非常夸张。每个member要存字符串本身,跳表节点还要存多个前向指针,一个member的总体内存开销在60到100个字节左右。一亿个member,光是这个key就得吃掉6到10个GB的内存。这还只是纯数据,不算Redis自身的元数据和碎片开销。

就算你咬牙给了内存,还有两个问题。第一是大Key阻塞,包含一亿个元素的ZSet在做持久化(RDB快照生成、AOF重写)或者主从全量同步时,需要遍历整个结构,期间Redis主线程的阻塞风险很高。第二是单一热点,所有排行查询都打在一个key上,这个key所在的分片CPU会率先打满,其他分片还很闲,集群整体利用率严重失衡。单key方案在百万级以下非常优雅,但到了亿级,它自己的优秀特性反而变成了掣肘。

3. 核心方案设计:分片支撑写入,分段支撑查询

前面铺垫了这么多,其实都是为了引出这套主方案。我在亿级排行榜里最终采用的思路,可以用两句话概括:用分片解决单点写入和内存瓶颈,用分段解决TopN查询和排名计算的复杂度。这两者不矛盾,可以叠加使用。

3.1 按用户维度分片:永远不要尝试把1亿人装进同一个ZSet

分布式系统里最朴素也最有效的思想就是分而治之。既然一个ZSet装不下1亿人,那就拆成多个ZSet。这里的分片键我推荐用userId的哈希值,哈希取模之后,每个用户的数据只会稳定落在某一个分片上,不会出现一个用户的数据跨多个分片的情况,这样更新成绩、查询个人名次都只需要访问一个分片。

假设拆成256个分片,每片维护大约40万用户(按一亿总量),这个量级的ZSet非常轻松,内存占用几百MB,写入和读取都在毫秒级完成。分片本身可以分布在同一个Redis实例的不同key里,也可以分布到Redis Cluster的不同节点上。分布在不同节点上的扩展性更好,如果未来用户量翻倍,可以把分片系数从256扩到512甚至1024,数据再做一次迁移就行。哈希取模在扩容的时候会有大面积数据重分布,但从256到512这个场景下,如果提前预留足够大的分片数量,很多业务就不需要真的扩容,分片数直接定死就行。

这里要注意一个细节:分片数不能随便拍脑袋。分片太少,单key数据量还是过大;分片太多,跨分片合并查询时网络开销会明显上升。一个ZSet控制在几十万到百万级member是比较合理的。一亿用户,256到512个分片是甜点区间。我自己的经验公式是按目标用户数的上限除以50到80万来定分片数,然后向上取整到2的幂次,方便哈希取模。

3.2 按分数段查询:TopN榜单的每个请求只扫描“高分区”

分片解决了存储和写入问题,但带来了一个新的查询问题:排行榜按分数降序排,可一亿用户经过哈希分片之后,分数高的用户被均匀打散到了所有分片里。每次查询Top100,你得把256个分片各自的Top100全捞出来,再做一次256路的归并排序,这一轮下来Redis操作就要几百次,虽然单次都很轻,但整体延迟和网络往返数还是会比单key方案慢不少。

所以在这个环节,我引入了一个“分数段”的分层缓存结构。思路是这样的:分片内部依然全量存用户的精确分数,但每个分片额外维护一个分数段桶计数。比如把分数区间从0到100万分按每1万分切一个桶,记录每个分数段里有多少个用户。

查询Top100的时候,先从分数段计数里定位到最高分区,假设最高的非空分数段是90万到100万这个桶,那就只需要去每个分片里查这个分数段的用户,再合并排序。其实不需要查全部256个分片,因为高分段用户人数极少,只要在少数几个包含高分段用户的分片里查询即可,当然实现上为了简单,我通常还是查所有分片,但每个分片查询的数据范围已经缩小到一个很小的分数区间,压力不大。如果某个高分段的人数超过100,那就直接在这个段内取前100,完全不用看低分段的数据。这个改动看着不大,但查询复杂度直接从“全量扫描+合并”降到了“锚定高分区+局部扫描”,效果是几何级的。

分数段的粒度也不是拍脑袋定的。段位太粗,一个段里还是几百万人,查询还是要大范围扫描;段位太细,分段计数本身的内存和更新开销会变大。我的实践值是让每个分数段的覆盖宽度差不多是TopN查询所需数量的5到10倍。比如Top100的榜单,每分钟大概有300到1000人落在同一个分数段内就是合适的粒度。具体如何计算段内人数需要结合业务实际,如果分数分布集中,可以再细化;分布分散就可以放宽。

3.3 个人排名的两种计算路径

个人名次的查询比TopN榜复杂一点,但也没那么玄乎。精确计算个人名次时,先查目标用户所在的分数段,取到用户在全局的高一档分数段累计人数,再去用户所在分片里算局部排名,两者相加。这个过程的复杂度可以认为是对数级加常数级,跟总用户量基本解耦,非常稳。

不过全量精确排名在亿级场景下有一个隐藏问题:如果所有用户同时查询个人排名,每个请求都要跨分片取计数,压力还是不小。所以实际方案里,我对个人排名做了分级处理。只有前N名(这部分大约百万级用户)支持精确排名,其余用户排名结果用“近似排名 + 百分比区间”来代替。比如系统返回“您当前排名约1280万,超过了87.3%的用户”,这种粗粒度结果用户完全能接受,而且成本比精确排名低两个数量级。这个思路核心就是:不为没人看的尾部数据付出头部数据的成本。

4. 排序细节与一致性:越简单越容易出错的地方

架构定了,真正考验工程师的反而是那些排序语义、并发一致性、边界情况。这些细节出了问题,用户端体验会非常糟糕,而且排查起来特别费劲。

4.1 同分排序规则:不要靠修改score来硬凑

排行榜业务里最常见的一个争执就是同分怎么办。用户A和用户B都是1000分,谁排前面?产品在这一点上的需求经常会变,今天要“先到先得”,明天要“队伍优先”,后天可能又改回“并列显示”。

最容易踩的坑是直接在score上叠加额外信息,比如把score设计成“总分*1e9 + 时间戳剩余位”这种复合数值。这样做确实能保证排序符合预期,但也带来了两个隐患:一是如果用户实际分数很大,脑子一算就可能溢出ZSet的double精度;二是分数更新的时候要把整个复合值拆解再拼装,逻辑复杂不说,还容易引入bug。

我的建议是不要在score上动太多脑筋,而是把同分的排序规则放到业务层或查询层去处理。Redis ZSet本身在memberscore相同的情况下,是按member名字的字典序来排的。既然排序规则这么简单且可控,产品如果要求“先到先得”,我们可以设计成member是“userId+初始时间戳”的复合体,或者单独维护一个二维排序的依据。具体工程设计上,可以通过给member名加前缀、或者在另外一张表里维护参与排名的用户ID与初始参与时间的映射,来实现时间维度的同分排序。虽然多占了一点额外空间,却能避免把score搞得一团糟。用score只做排名的大局切分,同分细节尽可能放到数据建模层面解决。

4.2 加分的原子性与主从延迟:一点都含糊不得

排行榜的更新操作非常高频,用户每产生一次行为就ZINCRBY一次。这里容易出问题的是“先查后改”的逻辑,比如某些场景要判断用户当前分数是否达到某个阈值再决定加多少,这就涉及读改写三步。如果不用原子操作或者Lua脚本把这几个操作串起来,并发一高就会出现重复加分、加错分的情况。我强烈建议所有涉及排行榜的写操作都收敛到一个更新接口里,用Lua脚本保证原子性,避免在业务代码里拼凑多条Redis命令。

另一个容易翻车的是主从延迟。如果架构里用了读写分离,用户刚加完分立刻查榜单,结果因为从库还没同步到最新数据而查不到自己的新分数,这种体验非常伤。所以排行榜这种实时性要求高的系统,我通常是直连主库读,或者写完后强制走主库读一次,不要抱侥幸心理。当然如果做了分片,每个分片的副本之间也存在同样问题,规则是一样的:实时查询走主,离线统计走从。

4.3 存储层的降级与过期:别让历史包袱拖垮线上

排行榜的数据还有一个特点,就是它永远在增长,但真正热的数据永远只有顶部那一小撮。如果整个榜单所有用户的记录都永远留在Redis里,内存迟早会爆。所以需要一套“热冷分层”的过期策略。

我一般会把排行榜数据分成三层。第一层是热数据,覆盖Top1000的头部,实时精确维护,常驻内存。第二层是温数据,覆盖Top100万以内的用户,保留分数但不保证排名严格实时,内存里存储,有一定延迟地更新。第三层是冷数据,即100万名之后的绝大多数用户,不保留精确排名,只保留分数段计数,具体分数回源到数据库或离线数仓。这三层之间的数据会通过定时任务或消息队列做迁移,保证Redis里的数据永远是最大化性价比的那部分。

这里有一个常见的尴尬场景:某些榜单是周期性的,比如“本月排行榜”。每个月结束,榜单要清零重新计。如果不做处理,老数据就会一直堆在Redis里,越积越多。最好的做法是用有效期设计,榜单的key带上业务周期后缀,周期结束后整区间下线和删除,成本非常低,比逐条清理用户数据快了太多。

5. 完整链路落地:从Redis数据结构到上层接口的工程化实现

理论讲再多,不落地都是空中楼阁。我拿一个实际的例子来讲:假设业务是一款棋牌类游戏的全服等级榜,注册用户1.2亿,活跃用户800万,核心需求是Top200榜实时展示、个人名次实时查询,分数区间在0到100万等级经验之间。我直接说一下这套方案落地时候的完整结构。

5.1 整体架构与Redis key设计

先看分片:基数按活跃量级定,我给这个场景定的是128个分片,每个分片覆盖大约60万活跃用户,加上1.2亿总注册里那些“沉睡用户”的数据会走冷数据通道,不会全部压到Redis里。

key的规划大致是这样的:

  • 主数据key:rank:main:{shardId},类型ZSet,存用户ID和当前经验值,这是最核心的数据。
  • 分段计数key:rank:segment:{shardId},类型Hash,字段是分段编号,值是当前分段人数,用于快速定位高分段。
  • 全局分段汇总key:rank:segment:global,类型Hash,是所有分片分段计数的聚合,后台任务定时累加刷新。

查询Top200的过程就变得很紧凑:

  1. rank:segment:global里找到当前最高且人数足够的分数段。
  2. 并发从128个分片的对应分段里取出局部TopN,每个分片取的数据量可以稍微多拉一点,比如取Top50,128片合计6400条。
  3. 在本地做归并排序,取前200。
  4. 如果Top200里跨了多个分数段,就多取几个段再归并一次。

个人想要查自己的名次(假设这个“自己”是普通中腰部玩家)也可以直接走精确排名的逻辑:高分段累计计数加局部排名。如果被查的用户落在了冷数据区,就只返回百分比区间,不返回精确名次。

有一些细节要注意,比如并发拉取分片的时候到底谁去拉、是按shardId并行还是串行,这取决于你所在团队的技术选型和资源状况。考虑到实现难度,串行也并非不能接受,因为128个分片即便串行,如果平均延迟控制在0.5毫秒,一轮也就60多毫秒,还在可接受范围。性能要求再严格一点,就可以做并行拉取,整体延迟能控制在20毫秒以内。

5.2 Lua脚本保证多步骤操作的原子性

写逻辑这块我再展开一点,因为并发加分时如果不做原子控制,分数准确性会被破坏。推荐把“更新分数 + 更新分片分段计数”这两步合并放到一个Lua脚本里执行,不要拆成两条Redis命令在业务代码里调两次。下面是一个简化的脚本示意:

-- KEYS[1]: rank:main:{shardId} 用户主ZSet -- KEYS[2]: rank:segment:{shardId} 分段计数Hash -- ARGV[1]: userId -- ARGV[2]: 分数增量 -- ARGV[3]: 旧分数所属的分段编号 -- ARGV[4]: 新分数所属的分段编号 local oldScore = redis.call('ZSCORE', KEYS[1], ARGV[1]) if oldScore == false then oldScore = 0 end local newScore = oldScore + tonumber(ARGV[2]) redis.call('ZADD', KEYS[1], newScore, ARGV[1]) -- 如果分段发生变化,更新分段计数 if ARGV[3] ~= ARGV[4] then redis.call('HINCRBY', KEYS[2], ARGV[3], -1) redis.call('HINCRBY', KEYS[2], ARGV[4], 1) end return newScore

脚本看起来简单,但有几个点要提醒。分段编号(ARGV[3]和ARGV[4])必须在业务代码里就算好,不能放到Redis里现算。我们一般规定分段编号等于“floor(score / 10000)”,这样可以保证分段计数和主ZSet里的实际分数永远对得上,即使某一次分段计数数据异常,也可以从全量ZSet重建。脚本在Redis里执行时是原子的,所以不用担心并发问题。

还有一个容易犯的错是分段计数更新时的负数问题。比如旧分段计数HINCRBY减1时如果发现减成负数了,那说明分段计数和主数据不一致了。这时候不要直接报错,而是记录一个异常日志,交给后台的校准任务去修复。排行榜这种系统很容易积累这种小偏差,定期巡检校准是必须的。

5.3 数据预热、冷启动和周期归档

榜单上线前,还有一个经常被忽略的冷启动问题。如果直接拿历史数据一次性灌进Redis,写入量过大,很容易拖垮线上。我的做法是分批预热,按分数段从高到低往Redis里灌,优先保证最高的几个分段能先查。预热期间如果来了线上读请求,因为低分段还没灌完,会返回一个“排名计算中”或粗略结果,体验上可以稍微打点折扣,但系统不会崩。这个方案比冰火两重天式的全量导入安全得多。

周期归档方面,每个周期结束时把Redis里的排行榜快照同步一份到离线存储做历史归档,然后直接用DEL删除当期key。删除大key的时候要注意Redis的阻塞问题,超过百万元素的key直接DEL可能会卡主线程几百毫秒,建议用SCAN分批删,或者用UNLINK异步删除。这些小技巧不复杂,但对线上稳定性影响非常大。

6. 查漏补缺:压测数据、真实故障和问题速查

方案落地后,上线前一定要做一轮压测和故障演练。这部分我分享几个实际用过的指标和踩过的坑,能帮你们少走一些弯路。

6.1 压测指标怎么定,内存怎么估算

写操作的压测目标,我习惯按峰值QPS的三倍来定。假设业务常说的高峰是每秒5000次加分操作,压测就要跑到每秒15000次写,持续压10分钟以上看有没有内存抖动或慢请求。因为排行榜的更新链路极短,瓶颈往往不在Redis而在网络带宽,所以压测的时候一定要监控客户端机器的网卡流量和Redis节点的带宽,别把带宽打满了还以为是Redis处理不过来。

内存估算有个简单公式,可以提前计算一个分片大概需要多少内存。每个用户在主ZSet里占约70字节,一个60万活跃用户的分片,主ZSet大约消耗42MB,分段计数Hash占很小,一个分片总内存控制在60MB以内,128个分片全量占用约7.6GB。这台机器选16GB内存的实例其实已经宽裕了。如果评估后发现内存超了,优先排查自己是不是把低活跃用户也塞进了热key,或者member的字符串长度是不是刻意加长了。

6.2 线上遇到过的三个典型故障

故障一:分段计数不准,导致TopN榜单出现名次跳号。原因是某一次并发加分时,应用层计算的旧分段编号和新分段编号是拿旧分数算的,但执行Lua脚本时主ZSet里的分数已经被另一条命令更新了,分段计数就会漏减。后来我们把分段编号的计算逻辑改成在Lua脚本内部根据最新分数自行计算,彻底解决了这个问题。这也是为什么我上面强调“分段编号必须在业务代码里算好”——其实更严谨的做法是在Lua脚本里算。两种方式都行,但一定要选一种并且理解它的边界,避免两头都靠结果两头都靠不住。

故障二:Redis分片节点分摊不均,某个分片内存明显偏大。排查下来发现是产品做了一个活动,大量新用户涌入时userId的哈希取模导致某几个shardId恰好分到了大批量导入的用户。这个问题的根源在于分片键的选择和用户的分布特征耦合。后续我们把分片键从userId哈希改成了userId和注册时间组合哈希,并且给导入任务加了预热限流,没有再出现这个问题。

故障三:周期性榜单清空时,直接DEL大key导致Redis阻塞几秒钟,业务瞬时超时。当时还没注意到UNLINK这个特性,直接在线上执行了DEL,结果损失了不少QPS。现在所有超过10万元素的key,清空一律用UNLINK,问题再也没有出现。

6.3 一套可以直接抄的“排行榜问题速查表”

症状可能原因排查手段解决方案
个人排名查询很慢未走分段计数,全量扫描了ZSet查看Redis慢日志,确认命令是ZRANGE还是ZREVRANK改为分段计数 + 局部排名方案
TopN查询超时分数段粒度过粗,段内人数太多查看segment计数,看单个分段人数是否异常加密分段粒度,或对主分片加读副本
分数更新丢失分片键设计不合理或未用Lua原子更新对比业务日志和Redis里的最终分数改用Lua脚本原子更新,分片键增加随机因子
内存持续增长未做冷热分离,低活跃用户全量驻留统计ZSet成员数与活跃用户数差异落地三层冷热分层和过期归档
同分排名忽前忽后依赖ZSet字典序,但member拼装了动态字段检查member组成规则固定member命名方案,同分规则在业务层排序
清榜或淘汰大key卡顿直接执行DEL查看Redis阻塞日志改用UNLINK异步删除,或分批SCAN删除
跨分片合并结果不对每个分片拉取的TopN数量不够,导致归并后丢数据核对每个分片返回条数和合并结果每个分片多拉20%~50%的数据再归并
活动上线后榜单更新变慢预热不充分,大量历史数据一次性灌入查看写入QPS和延迟曲线分批预热,高分段优先进内存,加限流

这套速查表是我每次排查排行榜问题时的第一参照物,很多问题看症状就能直接锁定方向,不需要每次从头捋一遍链路。

6.4 方案之外的几条实战感悟

最后说几条没法写在代码里的经验。

排行榜方案没有银弹,全量精确、实时、低成本这三个诉求在亿级规模下最多只能满足两个。我在项目评审时习惯直接问产品一个问题:用户真的需要看到自己的精确排名吗?得到的回答很多时候是“不需要”,他们需要的只是“被重视的感觉”。这个时候用百分比、段位、星座分组这些替代方案,技术上省一个量级,产品体验反而不降。

如果确认必须要全量精确排名,也建议设置一个“排名查询冷却时间”,比如用户一天最多查5次精确名次。这在业务上很合理,因为用户的分数不会在几分钟内天翻地覆,但系统压力却能明显下降。

还有一个很重要的原则:排行榜永远是附加功能,不是业务核心链路。任何时候都不要因为榜单查询拖垮了主流程。所以降级策略一定要提前设计好,Redis抖动的时候就停掉精确排名展示,返回上一次缓存的快照,或者直接显示“排名更新中”。这比强行查询然后把错误页面甩给用户好太多。

做亿级排行榜这件事,技术本身并不神秘,核心就是分而治之和冷热分离这两个思想,加上足够的细节敬畏。把最核心的读写路径抠到极致,再对降级、扩容、监控和一致性边界有完整的预案,这个方案就能经得住业务高峰的考验。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/17 3:19:50

GPT-5.2Pro证明埃尔德什猜想?陶哲轩:陷阱存在但AI未犯错

大概两天前,我刷到一条让我在电脑前愣了好一阵的消息:GPT-5.2Pro声称独立证明了一个悬置45年的数论猜想——埃尔德什猜想。更抓眼的是,菲尔茨奖得主陶哲轩转发了相关讨论,原话大意是“其中存在陷阱,但AI没犯错”。这组…

作者头像 李华
网站建设 2026/9/17 3:19:23

WinApps 快速上手:3 步在 Linux 上原生运行 Office 等 Windows 应用

WinApps 快速上手:3 步在 Linux 上原生运行 Office 等 Windows 应用 【免费下载链接】winapps Run Windows apps such as Microsoft Office/Adobe in Linux (Ubuntu/Fedora) and GNOME/KDE as if they were a part of the native OS, including Nautilus integrati…

作者头像 李华
网站建设 2026/9/17 3:17:20

截图工具PicPick电脑版

链接:https://pan.quark.cn/s/00bfe98ac885PicPick是一款非常优秀的截图工具,它的体积十分小巧,便携易用,功能却有非常多的花样,相比系统的,甚至一般第三方截图工具要强大的多,可以说是截图的首…

作者头像 李华