游戏排行榜系统的架构设计从Redis Sorted Set到分布式Top-K方案一、排行榜的业务特征与技术挑战排行榜是游戏中最具社交属性的系统之一。它不只是展示谁是第一更是驱动玩家活跃和付费的核心杠杆——段位排名、赛季结算、好友比拼、全服竞速每一种排行榜都在制造竞争感和目标感。从技术视角拆解游戏排行榜有几个显著特征写多读也多。以千万DAU的游戏为例假设每个玩家平均每小时产生3次分数变更事件那么写入TPS在800010000之间而排行榜的读取查看排名、翻页、查看好友排名可能达到写入的510倍。实时性要求分级。全服排行榜可以接受秒级延迟但好友榜的更新需要在百毫秒内生效否则会出现我刚打了最高分为什么好友看不到的客诉。维度爆炸。同一个玩家可能出现在战力榜、竞技榜、公会榜、赛季榜等十几个榜单中每个榜单的排序规则和更新频率都不同。二、Redis Sorted Set的极限与超越Redis Sorted Set是排行榜的标准答案——ZADD更新分数O(log N)ZREVRANGE查询Top-K也是O(log N K)。对于百万级用户的全服榜单个Redis实例足以承载。但当用户量达到千万级别时问题开始浮现内存膨胀。Sorted Set的底层是跳表字典每个元素的meta开销约80字节。一亿条记录memberscore大约需要8-12GB内存这让纯内存方案的成本变得不可接受。写入热点。所有写入操作集中在一个实例上Redis单线程模型下ZADD的TPS上限约为8-12万取决于value大小但在游戏高峰期可能成为瓶颈。跨分片排名不准。最简单的分片方式是按玩家ID哈希分布到多个Redis实例但这导致无法直接获取全局排名——玩家在分片内排名第3但全局可能排第1000。解决方案是分片排名定期合并的混合架构public class DistributedLeaderboard { private final ListRedisClient shards; private final int shardCount; private final String leaderboardKey; // 全局Top-1000缓存每30秒重建 private volatile ListRankEntry globalTopCache; private final ScheduledExecutorService mergeExecutor; public DistributedLeaderboard(ListRedisClient shards, String leaderboardKey) { this.shards shards; this.shardCount shards.size(); this.leaderboardKey leaderboardKey; this.mergeExecutor Executors.newSingleThreadScheduledExecutor(); // 定期合并 this.mergeExecutor.scheduleAtFixedRate( this::rebuildGlobalTopCache, 5, 30, TimeUnit.SECONDS); } public void updateScore(String playerId, double score) { int shardIndex Math.abs(playerId.hashCode()) % shardCount; shards.get(shardIndex).zadd(leaderboardKey, score, playerId); } public ListRankEntry getGlobalTopK(int k) { if (k globalTopCache.size()) { return globalTopCache.subList(0, Math.min(k, globalTopCache.size())); } // 超出缓存范围的请求走实时合并 return mergeTopKFromAllShards(k); } public long getPlayerGlobalRank(String playerId) { int playerShard Math.abs(playerId.hashCode()) % shardCount; Double playerScore shards.get(playerShard).zscore(leaderboardKey, playerId); if (playerScore null) return -1; // 汇总所有分片中分数高于该玩家的数量 long rank 0; for (int i 0; i shardCount; i) { if (i playerShard) { rank shards.get(i).zcount(leaderboardKey, Range.greaterThan(playerScore)); } else { rank shards.get(i).zcount(leaderboardKey, Range.greaterThan(playerScore)); } } return rank 1; // rank从1开始 } private void rebuildGlobalTopCache() { // 从每个分片拉取Top-K归并排序 ListCompletableFutureListRankEntry futures new ArrayList(); for (RedisClient shard : shards) { futures.add(CompletableFuture.supplyAsync(() - shard.zrevrangeWithScores(leaderboardKey, 0, 999) )); } this.globalTopCache futures.stream() .map(CompletableFuture::join) .flatMap(List::stream) .sorted(Comparator.comparingDouble(RankEntry::getScore).reversed()) .limit(1000) .collect(Collectors.toList()); } }这种方案下Top-K查询K≤1000走本地缓存延迟1ms玩家个人排名查询需要跨分片汇总延迟约10-30ms取决于分片数量写入延迟始终是单个分片的O(log N)没有扩大。三、赛季切换的数据归档与重置赛季制排行榜引入了一个独特挑战赛季结束时当前榜单数据需要归档同时为新赛季创建空榜单。这个过程必须在维护窗口内完成且不能丢失任何赛季末最后一刻的分数更新。设计上采用双写快照切换策略public class SeasonRotationManager { public void rotateSeason(String leaderboardKey, int oldSeasonId, int newSeasonId) { String archiveKey leaderboardKey :season: oldSeasonId; String newSeasonKey leaderboardKey :season: newSeasonId; // Step 1: RENAME原子操作旧榜单变为归档 // RENAME是原子操作不存在切换间隙 redis.rename(leaderboardKey, archiveKey); // Step 2: 归档数据压缩存储 byte[] compressed compressLeaderboardData(redis.dump(archiveKey)); // Step 3: 持久化到对象存储用于后续赛季回顾功能 ossClient.putObject(leaderboard-archive/season- oldSeasonId .dat, compressed); // Step 4: 设置归档数据的TTLRedis中保留30天热数据 redis.expire(archiveKey, 30, TimeUnit.DAYS); // Step 5: 新赛季榜单已就绪空Sorted Set由RENAME自动创建 // 此时新的写入会进入新赛季榜单 } }关键点Redis的RENAME命令是原子操作不会出现既没有旧榜单也没有新榜单的中间状态。如果写入请求恰好在RENAME之前到达分数会随旧榜单一起归档如果恰好在之后到达会写入新榜单。不会丢失数据。四、好友排行榜的实时推送好友排行榜的数据量小通常几十到几百人但实时性要求极高。方案是将好友榜数据加载到本地内存通过事件驱动保证一致性Service public class FriendLeaderboardService { // 每个玩家的好友榜缓存在本地内存 private final CacheLong, ListFriendRankEntry friendRankCache Caffeine.newBuilder() .maximumSize(500_000) .expireAfterWrite(5, TimeUnit.MINUTES) .build(); KafkaListener(topics player-score-changed) public void onScoreChanged(ScoreChangeEvent event) { // 找到该玩家的所有好友失效他们的好友榜缓存 ListLong friendIds friendGraphService.getFriendIds(event.getPlayerId()); friendIds.forEach(friendRankCache::invalidate); // 如果好友在线主动推送排名变化 friendIds.stream() .filter(onlinePlayerRegistry::isOnline) .forEach(fid - pushGateway.push(fid, new RankChangeNotification(event.getPlayerId(), event.getNewScore(), event.getRankDelta()))); } }五、总结排行榜系统看似简单——一个Sorted Set就能跑起来——但在千万级用户的游戏场景下需要解决的问题远不止数据结构的选型。分片排名的近似查询、赛季切换的原子性保证、好友榜单的实时推送、多维榜单的存储爆炸每一个都是需要仔细权衡的工程决策。核心经验是不要试图用一套方案解决所有榜单需求。全服榜追求全局一致性适合定期合并的分片方案好友榜追求实时性适合内存缓存事件驱动赛季榜追求历史可追溯适合归档到对象存储。针对不同的访问模式和一致性要求选择不同的架构才是排行榜系统设计的正确姿势。
企业数字化 ERP 产品动态
相关推荐
GPU在AI训练中的核心作用与配置优化 1. GPU在AI训练中的核心作用GPU(Graphics Processing Unit)最初是为图形渲染设计的专用电子电路,但其强大的并行计算能力使其成为现代人工智能训练的关键硬件。与CPU不同,GPU拥有数千个处理核心,能够同时执行大量数学运… · 2026/9/25 0:43:22
安卓真机root方案:闪动校园位置模拟与传感器数据同步技术解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/27 6:19:49
Jenkins集成部署实战:从插件配置到构建回滚的完整指南 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/27 6:19:43
荣耀出征正版官方客户端下载指引,忆往游戏正规安全渠道指南 《荣耀出征》由安徽游昕网络科技有限公司联合忆往游戏平台负责运营,是经过正版授权打造的奇迹 MU 怀旧手游。现阶段游戏依托专属官方主站面向全网正式开放,高度复刻奇迹 MU 端游原版内容,坚持绿色公平长久的运营模式,还原端游时期… · 2026/9/27 6:19:36
成都网站建设索q479185700报价单拆解与源码下载避坑指南 成都网站建设索q479185700报价单拆解与源码下载避坑指南 备案流程一头雾水,是很多初创企业找成都本地建站团队时最先碰到的墙。你明明只想快速上线个官网,结果被域名实名认证、ICP备案号、服务器IP备案这些术语绕得头晕,更别提还要担心对方… · 2026/9/27 6:19:36
云智变AI降重降AIGC:你改了一晚上同义词,为什么检测系统连看都没看一眼? 云智变AI官网:www.yunzhibian.cn | 微信公众号搜一搜:云智变AI学术
这篇文章想先跟你说一个“反常识”的事实:你把“重要”改成“关键”,把“因此”改成“所以”,把“综上所述”改成“总的来看”——AIGC检… · 2026/9/27 6:19:36
MATLAB雷达信号脉冲压缩仿真:LFM线性调频、匹配滤波与距离分辨率实现 简介:这套Matlab仿真工具完整呈现雷达信号脉冲压缩过程,从线性调频(LFM)信号生成、目标回波仿真到匹配滤波压缩处理均有可运行代码支撑,面向电子信息工程、计算机、数学等专业学生,适用于课程设计、期末大作… · 2026/9/27 0:00:01
汕头网站建设制作厂家避坑指南:5大注意事项救急 汕头网站建设制作厂家避坑指南:5大注意事项救急 改个需求建站公司拖一周,这种憋屈事我见得太多了。 很多汕头老板找本地建站团队,签合同前看着方案挺美,一上线就变脸。 今天不聊虚的,直接拆解找 汕头网站建设制作厂家 时的5个核心 注意事项… · 2026/9/27 0:00:01
多模态虚假新闻检测实战:BERT+ResNet双塔与对比学习 简介:基于PyTorch的多模态虚假新闻检测项目完整代码包,面向自然语言处理与计算机视觉交叉方向的开发者、科研人员及毕业设计选题者,解决社交媒体中文本与图像联合识别虚假新闻的问题。系统以BERT预训练模型提取文本语义特征,以Res… · 2026/9/27 0:00:01
MATLAB雷达信号脉冲压缩仿真:LFM线性调频、匹配滤波与距离分辨率实现 简介:这套Matlab仿真工具完整呈现雷达信号脉冲压缩过程,从线性调频(LFM)信号生成、目标回波仿真到匹配滤波压缩处理均有可运行代码支撑,面向电子信息工程、计算机、数学等专业学生,适用于课程设计、期末大作… · 2026/9/27 0:00:01
汕头网站建设制作厂家避坑指南:5大注意事项救急 汕头网站建设制作厂家避坑指南:5大注意事项救急 改个需求建站公司拖一周,这种憋屈事我见得太多了。 很多汕头老板找本地建站团队,签合同前看着方案挺美,一上线就变脸。 今天不聊虚的,直接拆解找 汕头网站建设制作厂家 时的5个核心 注意事项… · 2026/9/27 0:00:01
多模态虚假新闻检测实战:BERT+ResNet双塔与对比学习 简介:基于PyTorch的多模态虚假新闻检测项目完整代码包,面向自然语言处理与计算机视觉交叉方向的开发者、科研人员及毕业设计选题者,解决社交媒体中文本与图像联合识别虚假新闻的问题。系统以BERT预训练模型提取文本语义特征,以Res… · 2026/9/27 0:00:01