Gaming Leaderboard — 游戏排行榜
V1「做个答题竞赛的排行榜」
Section titled “V1「做个答题竞赛的排行榜」”公司搞了个内部知识竞赛,HR 找到你说:“能不能做个排行榜页面?大家答完题自己录入成绩,实时看排名。今天下午就要用。“时间紧,参赛人数就几十个人,不需要服务器。你决定用最简单的方案:一个 HTML 页面,分数存 localStorage,JS 排序后展示。反正都在同一台投影大屏上看,本地数据就够了。
你要解决什么
Section titled “你要解决什么”做一个纯本地的排行榜页面,支持录入分数、实时排名显示。
给 AI 的 Prompt
Section titled “给 AI 的 Prompt”做一个答题竞赛排行榜页面,要求:
技术栈:单个 HTML 文件,内联 CSS + JavaScript,不依赖任何框架
功能:1. 顶部输入表单:姓名(文字)+ 分数(数字 0-100)+ 用时(秒数)+ 提交按钮2. 提交后数据存入 localStorage,key 为 "leaderboard",值为 JSON 数组3. 排行榜列表:按分数降序排列,分数相同按用时升序排列4. 显示 Top 20,每行展示:排名(带奖牌图标前三名)、姓名、分数、用时、提交时间5. 前三名背景高亮:金色、银色、铜色6. 底部显示统计信息:参赛人数、平均分、最高分、最低分7. "清空排行榜"按钮(需二次确认)8. 支持重复提交同一姓名,取最高分记录
防作弊(简单):- 分数必须 0-100 整数- 用时必须 > 0- 姓名不能为空或纯空格
UI 要求:- 整体风格偏游戏风,深色背景 + 霓虹色文字- 新提交的记录有一个闪烁动画效果- 排行榜有从上到下的渐入动画- 响应式:大屏幕上居中显示宽度 800px- 输入姓名和分数后提交,排行榜立刻更新
- 分数相同时按用时排序,用时短的排前面
- 同一姓名重复提交,保留最高分
- 前三名有对应颜色高亮和奖牌图标
- 统计信息(人数、平均分等)计算正确
- 刷新页面后数据不丢失
- 清空排行榜需要确认,确认后数据清除
- 非法输入(空姓名、负分、超过100分)被拒绝
你学到了什么
Section titled “你学到了什么”- JavaScript 数组排序:多字段排序的 comparator 写法 → M9(数据处理)
- localStorage 的容量限制和序列化开销 → M2(数据存储)
- 数据去重策略:同一 key 保留最优记录 → M9(数据处理)
V2「要全公司一起比,不能只看自己浏览器的排名」
Section titled “V2「要全公司一起比,不能只看自己浏览器的排名」”HR 说:“竞赛效果很好,下个月要全公司 500 人一起比!但这次每个人在自己电脑上答题,排行榜要是全公司共享的,不能只看自己浏览器里的。“你意识到 localStorage 是单机的,500 个人需要共享同一份排名数据。需要一个后端来接收所有人的分数并统一排序。先不上数据库,用 JSON 文件存就行,500 条数据而已。
你要解决什么
Section titled “你要解决什么”搭建后端 API 统一管理排行榜数据,所有用户共享同一份排名。
给 AI 的 Prompt
Section titled “给 AI 的 Prompt”在 V1 基础上增加后端实现全公司共享排行榜,要求:
后端:Go + Gin
数据存储:- 使用 JSON 文件(scores.json)存储所有分数记录- 用 sync.RWMutex 保护文件读写,防止并发损坏- 数据结构:[{name, department, score, time_spent, submitted_at}, ...]
API:1. POST /api/scores — 提交分数 - 请求体:{name, department, score, time_spent} - 验证:score 0-100,time_spent > 0,name 非空 - 同一姓名+部门组合视为同一人,保留最高分 - 返回该用户当前排名2. GET /api/leaderboard?top=50&department= — 获取排行榜 - 支持 top N 参数,默认 50 - 支持按部门筛选 - 返回排好序的数组 + 总参赛人数 + 各部门平均分3. GET /api/leaderboard/me?name=&department= — 查询个人排名 - 返回该用户排名、分数、超过了百分之多少的人4. GET /api/stats — 全局统计 - 总人数、平均分、中位数、各部门排名(按平均分)
数据保护:- 每次写入前备份上一版本到 scores.json.bak- 启动时如果 scores.json 损坏,尝试从 .bak 恢复
前端更新:- 移除 localStorage 逻辑,改为调 API- 增加部门下拉选择(技术部/产品部/市场部/运营部/设计部)- 增加"我的排名"卡片,显示个人排名和百分位- 排行榜每 10 秒自动刷新- 在两个不同浏览器提交分数,排行榜都能看到对方的数据
- 同一人重复提交取最高分
- 部门筛选正常工作
- 个人排名 API 返回正确的排名和百分位
- 模拟 10 个并发请求,JSON 文件内容不损坏
- 删除 scores.json,从 .bak 自动恢复
- 排行榜每 10 秒刷新看到最新数据
你学到了什么
Section titled “你学到了什么”- JSON 文件作为轻量级共享存储,适合小规模场景 → M2(数据存储)
- sync.RWMutex 读写锁:多读少写场景的并发控制 → M6(事务与一致性)
- 内存排序的时间复杂度:O(N log N) 每次请求都要排一次 → M9(数据处理)
- 数据备份与恢复的基本策略 → M3(数据复制)
V3「双十一答题活动1万人同时参与,排行榜刷新要等10秒,提交分数也卡」
Section titled “V3「双十一答题活动1万人同时参与,排行榜刷新要等10秒,提交分数也卡」”公司和电商平台合作搞了个双十一答题赢红包活动,预计 1 万人同时参与。上线第一天就炸了:排行榜页面加载要 10 秒,提交分数经常超时。你分析了原因:JSON 文件被一把大锁卡住,每次写入都要锁住整个文件;每次读取排行榜都要加载全部数据然后排序,1 万条记录排序开销不小;10 秒轮询 × 1 万用户 = 后端每秒 1000 个排行榜请求。你需要彻底重构数据层。
你要解决什么
Section titled “你要解决什么”用 Redis Sorted Set 替换 JSON 文件,实现高并发下的实时排行榜,并用 SSE 替代轮询。
给 AI 的 Prompt
Section titled “给 AI 的 Prompt”在 V2 基础上用 Redis 重构排行榜系统,解决万人并发问题,要求:
后端:Go + Gin + Redis + PostgreSQL
核心重构 — Redis Sorted Set:1. 排行榜数据用 Redis Sorted Set 存储 - key: leaderboard:global - member: "{name}:{department}" - score: 分数(Redis ZADD 天然去重,相同 member 更新为最高分用 ZADD GT)2. 提交分数: - ZADD GT leaderboard:global {score} "{name}:{department}" — O(log N) - ZADD GT leaderboard:dept:{dept} {score} "{name}:{department}" — 部门榜 - HINCRBY leaderboard:stats total_submissions 1 — 提交计数3. 获取排行榜: - ZREVRANGE leaderboard:global 0 49 WITHSCORES — Top 50,O(log N + 50) - ZREVRANK leaderboard:global "{name}:{department}" — 个人排名,O(log N) - ZCARD leaderboard:global — 总人数,O(1)4. 百分位计算: - rank / total * 100 = 超过百分之多少的人
数据持久化 — Write-Behind 模式:1. 每次提交分数写 Redis(实时),同时发消息到 Go channel2. 后台 goroutine 批量消费 channel,每 5 秒或累积 100 条写入 PostgreSQL3. PostgreSQL 表:scores(id, name, department, score, time_spent, submitted_at)4. 服务重启时如果 Redis 为空,从 PostgreSQL 重建 Sorted Set
实时推送 — SSE 替代轮询:1. GET /api/leaderboard/live — SSE endpoint - 后台 goroutine 每 3 秒从 Redis 读取 Top 20 变化 - 只在排名有变化时推送(对比上一次快照) - 推送格式:{type: "update", top20: [...], total: N, timestamp: ...}2. GET /api/leaderboard/live/me?name=&department= — 个人排名 SSE - 当个人排名变化时推送新排名
API(保留 HTTP 兼容):1. POST /api/scores — 提交分数(写 Redis + 异步写 PostgreSQL)2. GET /api/leaderboard?top=50&department= — HTTP 获取(兜底)3. GET /api/leaderboard/me?name=&department= — 个人排名4. GET /api/admin/performance — 性能指标 - Redis 操作平均延迟 - SSE 连接数 - PostgreSQL 写入队列长度 - 每秒提交数(QPS)
前端更新:- 连接 SSE,排行榜实时更新无需手动刷新- 排名变化时有上升/下降箭头动画- 显示"当前在线人数"(SSE 连接数)- 新记录进入 Top 20 时有弹幕效果- 用脚本并发提交 1000 条分数,全部成功且排行榜排序正确
- Redis ZCARD 返回正确的总人数
- ZREVRANGE 返回的 Top 20 排序正确
- 同一人提交更低分数后排名不变(ZADD GT 生效)
- SSE 连接建立后,有新提交时前端自动更新排行榜
- 停止 Redis,等待 PostgreSQL 写入完成,重启 Redis,从 PostgreSQL 恢复数据
- /api/admin/performance 返回合理的延迟指标
- 排行榜响应时间从 10 秒降到 < 50ms
你学到了什么
Section titled “你学到了什么”- Redis Sorted Set:O(log N) 插入 + O(log N + M) 范围查询,天生适合排行榜 → M4(缓存策略)
- ZADD GT 语义:只在新分数更高时更新,天然实现”保留最高分” → M2(数据存储)
- Write-Behind 模式:先写缓存再异步落库,用一致性换性能 → M4(缓存策略)
- SSE 替代轮询:1 万用户轮询 = 1000 QPS,SSE = 1 万长连接但推送按需 → M13(网络与内容分发)
- Go channel 做内存消息队列,批量写入降低数据库压力 → M5(消息与事件)
- 冷启动恢复:Redis 丢失时从 PostgreSQL 重建 → M3(数据复制)
- 性能从 O(N log N) 全量排序变为 O(log N) 增量更新的本质提升 → M8(可扩展性)
V4「要支持多个排行榜(日榜/周榜/总榜)和不同游戏」
Section titled “V4「要支持多个排行榜(日榜/周榜/总榜)和不同游戏」”答题活动大获成功,公司决定把排行榜系统做成通用平台,支持多款游戏和多个时间维度的排行榜。产品经理的需求清单:每个游戏有独立排行榜,每个排行榜分日榜、周榜、总榜;日榜每天零点自动重置,周榜每周一重置;管理后台能看到所有排行榜的概览。现有的单个 Sorted Set 结构远远不够了,你需要设计一套多维度排行榜架构。
你要解决什么
Section titled “你要解决什么”设计多游戏、多时间维度的排行榜体系,使用 Redis Sorted Set 按维度隔离,支持自动过期和排行榜注册管理。
给 AI 的 Prompt
Section titled “给 AI 的 Prompt”在 V3 基础上构建多维度排行榜平台,要求:
后端:Go + Gin + Redis + PostgreSQL
多维度 Sorted Set 设计:1. 排行榜 key 命名规范: - 日榜:leaderboard:{game_id}:daily:{date} — 如 leaderboard:quiz:daily:2024-01-15 - 周榜:leaderboard:{game_id}:weekly:{year}-W{week} — 如 leaderboard:quiz:weekly:2024-W03 - 总榜:leaderboard:{game_id}:alltime2. 提交分数时同时写入三个维度: - ZADD GT leaderboard:{game_id}:daily:{today} {score} {user_id} - ZADD GT leaderboard:{game_id}:weekly:{this_week} {score} {user_id} - ZADD GT leaderboard:{game_id}:alltime {score} {user_id} - 用 Redis Pipeline 批量执行,减少网络往返3. 自动过期: - 日榜 key 设置 TTL 48 小时(保留昨天的供对比) - 周榜 key 设置 TTL 15 天(保留上周的供对比) - 总榜永不过期4. 历史快照: - 日榜到期前将 Top 100 快照存入 PostgreSQL - 表:leaderboard_snapshots(id, game_id, dimension, period, rank, user_id, score, snapshot_at) - 支持查询历史任意一天/周的排行榜
排行榜注册管理:1. PostgreSQL 表:leaderboard_registry(id, game_id, game_name, description, score_type, created_at) - score_type: "highest"(取最高分)/ "cumulative"(累计分)/ "latest"(取最新分)2. API: - POST /api/admin/leaderboards — 注册新游戏排行榜 - GET /api/admin/leaderboards — 所有排行榜列表及当前参与人数 - DELETE /api/admin/leaderboards/:game_id — 删除排行榜及所有数据
通用排行榜 API:1. POST /api/games/{game_id}/scores — 提交分数 - 根据 score_type 选择 ZADD 策略:GT(最高分)、INCR(累计)、无标志(最新)2. GET /api/games/{game_id}/leaderboard?dimension=daily&date=2024-01-15&top=50 - dimension: daily/weekly/alltime - 返回排行榜 + 该维度总参与人数3. GET /api/games/{game_id}/leaderboard/me?user_id= — 个人在各维度的排名4. GET /api/games/{game_id}/leaderboard/history?dimension=daily&from=2024-01-01&to=2024-01-15 - 查询历史快照
管理仪表盘 API:- GET /api/admin/dashboard — 所有游戏排行榜概览 - 每个游戏:日/周/总榜参与人数、今日新增提交数、Top 1 玩家
前端:1. 排行榜页面顶部标签切换:日榜/周榜/总榜2. 左侧游戏选择栏,支持多款游戏切换3. 日榜显示日期选择器,可查看历史日期排名4. 个人中心显示在各维度的排名变化趋势图5. 管理后台:排行榜注册、数据概览、快照查看- 提交分数后日榜、周榜、总榜同时更新
- 日榜 key 在 48 小时后自动过期
- 日榜过期前 Top 100 快照成功存入 PostgreSQL
- 查询历史某天的排行榜返回正确的快照数据
- 不同游戏的排行榜完全独立,互不影响
- score_type 为 cumulative 时分数正确累加
- Redis Pipeline 批量写入,延迟低于单条写入的 3 倍
- 管理仪表盘正确显示所有游戏排行榜概览
你学到了什么
Section titled “你学到了什么”- Redis Sorted Set 的命名空间设计:key 命名规范化实现多维度隔离 → M4(缓存策略)
- TTL 自动过期实现排行榜”自动重置”,免去定时任务 → M4(缓存策略)
- Redis Pipeline 批量操作:减少网络往返开销 → M13(网络与内容分发)
- 快照模式:易失数据在过期前持久化,保留历史分析能力 → M2(数据存储)
- 不同计分策略(最高/累计/最新)的 ZADD 语义差异 → M9(数据处理)
- 注册表模式:元数据驱动的通用化系统设计 → M1(API 设计)
V5「活动期间万人同时提交分数,SSE连接数爆了」
Section titled “V5「活动期间万人同时提交分数,SSE连接数爆了」”多款游戏同时举办活动,峰值一万用户同时在线。两个问题暴露出来:第一,SSE 连接数暴涨到上万,单个服务器的文件描述符接近上限,内存被大量空闲连接占满;第二,分数提交 QPS 突破 5000,每次提交要写 3 个 Sorted Set,Redis 单连接开始出现排队。运维告警不断,排行榜更新明显延迟,部分用户 SSE 连接断开后重连形成”惊群效应”。
你要解决什么
Section titled “你要解决什么”优化 SSE 连接管理和消息分发机制,实现分数的批量写入,以及多实例部署下的实时推送方案。
给 AI 的 Prompt
Section titled “给 AI 的 Prompt”在 V4 基础上解决万人并发下的连接和写入瓶颈,要求:
后端:Go + Gin + Redis + PostgreSQL + Nginx
SSE 连接优化:1. 连接池化与分组 fan-out: - SSE 连接按排行榜维度分组:sseGroup:{game_id}:{dimension} - 后台 goroutine 每 3 秒计算一次排行榜变化,广播到对应分组 - 单个 goroutine 计算 → 广播给 N 个连接,而不是 N 个连接各自查 Redis2. 连接数管理: - 单实例最大 SSE 连接数限制 5000 - 超出后返回 503 + Retry-After 头 - 连接空闲超过 60 秒发送心跳 ping,超过 5 分钟无活跃关闭连接3. 慢客户端处理: - 每个 SSE 连接维护发送缓冲区(channel buffer 100) - 缓冲区满时丢弃旧消息,下次推送全量快照 - GET /api/leaderboard/snapshot/{game_id}/{dimension} — 慢客户端的兜底 HTTP 接口4. 重连优化: - SSE 消息携带 Last-Event-ID(时间戳) - 客户端重连时携带 Last-Event-ID,服务端只推送此后的增量变化 - 随机化重连延迟(1-5 秒),避免惊群效应
批量分数写入:1. 写入聚合层: - 分数提交不直接写 Redis,先写入内存 buffer - buffer 按 game_id 分桶,每桶每 100ms 或累积 50 条触发批量写入 - 用 Redis Pipeline 一次发送所有 ZADD 命令2. 写入确认: - POST /api/games/{game_id}/scores 立即返回 202 Accepted - 分数写入完成后通过 SSE 推送排名更新(用户看到自己排名变化即为确认)3. 异步持久化: - 批量写入 Redis 后,异步批量写入 PostgreSQL - PostgreSQL 使用 COPY 批量插入,替代逐条 INSERT
多实例部署:1. Nginx 负载均衡: - SSE 长连接用 ip_hash 策略(避免连接跨实例) - HTTP API 用 round_robin2. 跨实例排行榜变化通知: - 实例 A 的分数更新需要通知实例 B 的 SSE 连接 - 用 Redis Pub/Sub:channel leaderboard_updates:{game_id}:{dimension} - 每个实例订阅相关 channel,收到通知后推送给本实例的 SSE 连接3. WebSocket 可选升级: - GET /ws/leaderboard/{game_id} — 为需要双向通信的场景提供 WebSocket - 支持客户端主动请求刷新、切换关注的排行榜维度
监控指标:- SSE 连接数(按实例/按排行榜维度)- 写入 buffer 积压量和 flush 频率- Redis Pipeline 平均批次大小和延迟- 慢客户端丢弃消息次数- Pub/Sub 消息延迟
前端:1. SSE 重连逻辑:指数退避 + 随机抖动(1s、2s、4s + random(0,1s))2. 离线期间缓存最后一次快照,重连后对比更新3. 连接状态指示器:绿色在线、黄色重连中、红色断开4. 排名变化动画优化:批量更新时合并动画,避免频繁 DOM 操作- 模拟 5000 个 SSE 连接,服务器内存和 CPU 稳定
- 超过 5000 连接限制时返回 503 + Retry-After
- fan-out 广播正确工作:1 次计算推送到所有订阅连接
- 慢客户端缓冲区满后自动降级为全量快照推送
- 客户端重连携带 Last-Event-ID,收到增量更新而非全量
- 批量写入生效:100ms 窗口内的多次提交合并为 1 次 Pipeline
- 两个后端实例部署,实例 A 提交的分数通过 Pub/Sub 通知实例 B 的 SSE 连接
- 压测 5000 QPS 分数提交,排行榜更新延迟 < 1 秒
- PostgreSQL 批量写入用 COPY,写入性能提升
你学到了什么
Section titled “你学到了什么”- SSE 连接池化与 fan-out:单计算多广播,避免重复查询 → M8(可扩展性)
- 写入聚合(Write Batching):时间窗口 + 数量阈值触发批量写入 → M9(数据处理)
- Redis Pub/Sub 实现跨实例消息通知 → M5(消息与事件)
- 慢消费者处理策略:缓冲 + 丢弃 + 降级 → M13(网络与内容分发)
- 惊群效应与随机退避:分布式系统中的经典问题 → M8(可扩展性)
- 长连接的负载均衡策略:ip_hash vs round_robin 的选择 → M13(网络与内容分发)
- PostgreSQL COPY 批量写入的性能优势 → M2(数据存储)
V6「要支持好友排名、地区排名,组合维度太多」
Section titled “V6「要支持好友排名、地区排名,组合维度太多」”产品经理带来了新需求:“玩家不只想看全球排名,还想看自己在好友中排第几、在本省排第几。而且不同游戏还要能交叉——比如’上海地区本周答题排名’。“你算了一下:10 款游戏 × 3 个时间维度 × 30 个省份 × 好友关系,组合维度数以万计,不可能为每个组合都预建一个 Sorted Set。更麻烦的是好友排名——每个用户的好友列表不同,意味着排名是动态的。同时反作弊也成了紧迫问题,有人用脚本刷分影响公平性。
你要解决什么
Section titled “你要解决什么”设计组合维度排行榜(全球+好友+地区),在不为每个组合预建 Sorted Set 的情况下高效计算排名,同时引入反作弊分数验证管道。
给 AI 的 Prompt
Section titled “给 AI 的 Prompt”在 V5 基础上支持组合维度排行榜和反作弊机制,要求:
后端:Go + Gin + Redis + PostgreSQL + Kafka
组合维度排行榜:1. 预计算维度(高频查询,主动维护): - 全球排行:leaderboard:{game_id}:global:{dimension} — 每次提分更新 - 地区排行:leaderboard:{game_id}:region:{region_id}:{dimension} — 提分时根据用户地区更新 - 用户注册时绑定 region_id,每次提分自动写入对应地区排行榜2. 好友排名(动态计算,按需查询): - 不预建好友排行榜 Sorted Set(好友关系变化频繁,维护成本过高) - 查询逻辑: a. 获取用户好友列表(Redis Set:friends:{user_id}) b. 用 ZMSCORE 批量获取好友在全球榜中的分数 c. 内存中排序并计算用户在好友中的排名 - 缓存:好友排名结果缓存 30 秒(key: friend_rank:{user_id}:{game_id}:{dimension})3. 好友分数变更通知: - 用户提交分数后,检查该用户是否在其他人的好友列表中 - 通过 Redis Pub/Sub 通知相关用户的好友排名缓存失效 - 用反向索引 Redis Set:followers:{user_id} — 存储"谁把我加为好友"4. 懒计算的不活跃维度: - 冷门组合(如"西藏地区月度排行")不预计算 - 首次查询时实时从全球排行榜过滤计算,结果缓存 5 分钟 - GET /api/games/{game_id}/leaderboard/composite?dimension=weekly®ion=tibet&top=50
分片 Redis:1. 按游戏分片:不同游戏的排行榜数据存储在不同 Redis 实例 - shard 路由:hash(game_id) % shard_count - 分片配置存入 PostgreSQL:redis_shards(shard_id, host, port, game_ids)2. 好友关系数据单独一个 Redis 实例(跨游戏共享)3. 分片扩容方案:一致性 hash + 数据迁移工具
反作弊分数验证管道:1. 分数提交流程改造: - POST /api/games/{game_id}/scores 提交原始分数 + 游戏过程摘要(答题记录、用时分布等) - 立即写入 pending 状态,不直接进入排行榜2. 验证管道(Kafka Consumer): - Stage 1: 基础校验 — 分数范围、用时合理性、提交频率(同一用户 1 分钟内不超过 3 次) - Stage 2: 统计异常检测 — 分数与历史均值偏差超过 3 个标准差标记可疑 - Stage 3: 行为分析 — 答题用时分布是否合理(全部秒答判定为作弊) - 通过验证 → ZADD 写入排行榜 + 状态改为 confirmed - 未通过 → 标记为 rejected + 记录原因 + 通知管理员3. 申诉机制: - POST /api/scores/{score_id}/appeal — 玩家对 rejected 分数提交申诉 - 管理员审核后手动通过或维持拒绝
API 新增:1. GET /api/games/{game_id}/leaderboard/friends?user_id= — 好友排行榜2. GET /api/games/{game_id}/leaderboard/region/{region_id}?dimension= — 地区排行榜3. GET /api/admin/anticheat/stats — 反作弊统计:通过率、拒绝率、申诉率4. GET /api/admin/anticheat/suspicious — 可疑分数列表5. PUT /api/admin/anticheat/scores/{id}/verdict — 管理员审判
监控与可观测性:1. 各分片 Redis 负载均衡度2. 好友排名计算延迟分布3. 反作弊管道各阶段通过率和延迟4. Kafka Consumer lag 监控5. 组合维度查询的缓存命中率热力图
前端:1. 排行榜顶部增加维度切换:全球 / 好友 / 我的地区2. 好友排名旁显示好友在线状态和最近分数变化3. 分数被拒绝时弹出通知,显示原因和申诉按钮4. 地区排行榜地图可视化:中国地图上标注各省 Top 1 玩家- 提交分数后全球排行、地区排行同时更新
- 好友排名正确计算,添加/删除好友后排名变化
- 好友提交新分数后,相关用户的好友排名缓存失效并更新
- 冷门组合维度首次查询实时计算,结果被缓存,第二次命中缓存
- 不同游戏的数据分布在不同 Redis 分片上
- 反作弊管道正确拦截异常分数:秒答、超频提交、异常高分
- 正常分数通过验证后进入排行榜,延迟 < 2 秒
- 被拒绝的分数可以申诉,管理员审核后状态正确更新
- Kafka Consumer 处理速度跟得上提交速率,无明显积压
- 10 万用户量级下好友排名查询 P95 < 200ms
你学到了什么
Section titled “你学到了什么”- 预计算 vs 懒计算:高频维度主动维护、低频维度按需计算 → M4(缓存策略)
- 好友排行榜的设计权衡:不可能为每个用户预建 Sorted Set → M8(可扩展性)
- Redis 分片策略:按业务维度分片 + 一致性 hash → M8(可扩展性)
- 反向索引(followers)实现变更通知的高效查找 → M2(数据存储)
- 流式验证管道(Kafka):多阶段校验解耦提交与上榜 → M5(消息与事件)
- 反作弊的统计学方法:标准差检测异常值 → M9(数据处理)
- 分布式系统中”全量预算”不可行时的降级策略 → M16(DevOps)
- 多维度缓存的失效策略:事件驱动 + TTL 双重保障 → M4(缓存策略)
- 全链路可观测性:从提交到上榜的每个环节都需要监控 → M15(可观测性)