游戏排行榜系统的架构设计:从Redis Sorted Set到分布式Top-K方案
游戏排行榜系统的架构设计从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就能跑起来——但在千万级用户的游戏场景下需要解决的问题远不止数据结构的选型。分片排名的近似查询、赛季切换的原子性保证、好友榜单的实时推送、多维榜单的存储爆炸每一个都是需要仔细权衡的工程决策。核心经验是不要试图用一套方案解决所有榜单需求。全服榜追求全局一致性适合定期合并的分片方案好友榜追求实时性适合内存缓存事件驱动赛季榜追求历史可追溯适合归档到对象存储。针对不同的访问模式和一致性要求选择不同的架构才是排行榜系统设计的正确姿势。

相关新闻

AI工具组合不是拼图游戏:资深架构师手把手教你构建抗衰减、可审计、易迭代的智能工作栈

AI工具组合不是拼图游戏:资深架构师手把手教你构建抗衰减、可审计、易迭代的智能工作栈

更多请点击: https://intelliparadigm.com 第一章:AI工具组合不是拼图游戏:资深架构师手把手教你构建抗衰减、可审计、易迭代的智能工作栈 真正的智能工作栈不是把Copilot、LangChain、LlamaIndex和VectorDB简单堆叠——它是一套具备生命周期…

2026/9/14 18:42:36 阅读更多 →
猫抓浏览器扩展:3大核心功能助您高效获取网页媒体资源

猫抓浏览器扩展:3大核心功能助您高效获取网页媒体资源

猫抓浏览器扩展:3大核心功能助您高效获取网页媒体资源 【免费下载链接】cat-catch 猫抓 浏览器资源嗅探扩展 / cat-catch Browser Resource Sniffing Extension 项目地址: https://gitcode.com/GitHub_Trending/ca/cat-catch 您是否经常遇到网页视频无法下载…

2026/9/12 12:07:15 阅读更多 →
全球显示器支架底座市场发展战略规划及 现状调研报告2026年版

全球显示器支架底座市场发展战略规划及 现状调研报告2026年版

全球显示器支架底座市场发展战略规划及 现状调研报告2026年版显示器支架底座是为显示设备提供稳定支撑、固定与调节接口的核心基础部件,通常采用金属制成,搭配配重、夹持或穿孔结构实现可靠固定。产品兼容 VESA 标准接口,可匹配单屏、双屏及多…

2026/9/19 4:16:31 阅读更多 →

最新新闻

InvenTree 库存管理实践指南:从Docker部署到条码出入库

InvenTree 库存管理实践指南:从Docker部署到条码出入库

InvenTree 库存管理实践指南:从Docker部署到条码出入库 【免费下载链接】InvenTree Open Source Inventory Management System 项目地址: https://gitcode.com/GitHub_Trending/in/InvenTree InvenTree 是一款完全开源的库存管理与物料追踪系统,适…

2026/9/20 20:31:00 阅读更多 →
awesome-prompts 提示词库:377 条 GPT 提示词,从直接复制到改成自己的

awesome-prompts 提示词库:377 条 GPT 提示词,从直接复制到改成自己的

awesome-prompts 提示词库:377 条 GPT 提示词,从直接复制到改成自己的 【免费下载链接】awesome-prompts Curated list of chatgpt prompts from the top-rated GPTs in the GPTs Store. Prompt Engineering, prompt attack & prompt protect. Advan…

2026/9/20 20:31:00 阅读更多 →
Ace(Ajax.org Cloud9 Editor)嵌入、运行与构建完全指南

Ace(Ajax.org Cloud9 Editor)嵌入、运行与构建完全指南

Ace(Ajax.org Cloud9 Editor)嵌入、运行与构建完全指南 【免费下载链接】ace Ace (Ajax.org Cloud9 Editor) 项目地址: https://gitcode.com/gh_mirrors/ac/ace Ace 是一款用 JavaScript 编写的独立浏览器代码编辑器,目标是在浏览器中…

2026/9/20 20:31:00 阅读更多 →
基于TDGL相场模拟的电极效应对铁电薄膜畴结构影响研究

基于TDGL相场模拟的电极效应对铁电薄膜畴结构影响研究

简介:基于时变Ginzburg-Landau(TDGL)方法的铁电薄膜畴结构模拟资料,面向材料物理、微电子器件设计研究人员和工程师,重点解决不同表面电极分布对畴结构调控作用的问题。资源共1个PDF文件,压缩包大小788KB,内容以完整Py…

2026/9/20 20:31:00 阅读更多 →
AssetRipper Unity 资源提取完整指南:从黑盒游戏文件到可运行工程

AssetRipper Unity 资源提取完整指南:从黑盒游戏文件到可运行工程

AssetRipper Unity 资源提取完整指南:从黑盒游戏文件到可运行工程 【免费下载链接】AssetRipper GUI application to analyze game files 项目地址: https://gitcode.com/GitHub_Trending/as/AssetRipper AssetRipper 是一款用于分析 Unity 游戏文件的图形界…

2026/9/20 20:31:00 阅读更多 →
公司做网络推广哪个网站好?3年老兵揭秘真实建站报价与避坑指南

公司做网络推广哪个网站好?3年老兵揭秘真实建站报价与避坑指南

公司做网络推广哪个网站好?3年老兵揭秘真实建站报价与避坑指南 刚接手公司推广预算时,我对着“ICP备案”四个字发了半天呆,流程一头雾水,生怕填错一步就耽误上线。别慌,这行摸爬滚打10年,最懂这种焦虑。今天不聊虚的,直接拆解 建站报价 里的门道,告诉你 公司做网络推广哪个网站好…

2026/9/20 20:30:09 阅读更多 →

日新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/20 0:00:46 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/20 0:00:46 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/20 0:00:46 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/20 0:00:46 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/20 0:00:46 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/20 0:00:46 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/19 23:01:36 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/19 17:50:38 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/19 23:35:34 阅读更多 →