跳转到内容

Gaming Leaderboard — 游戏排行榜


公司搞了个内部知识竞赛,HR 找到你说:“能不能做个排行榜页面?大家答完题自己录入成绩,实时看排名。今天下午就要用。“时间紧,参赛人数就几十个人,不需要服务器。你决定用最简单的方案:一个 HTML 页面,分数存 localStorage,JS 排序后展示。反正都在同一台投影大屏上看,本地数据就够了。

做一个纯本地的排行榜页面,支持录入分数、实时排名显示。

做一个答题竞赛排行榜页面,要求:
技术栈:单个 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分)被拒绝
  • JavaScript 数组排序:多字段排序的 comparator 写法 → M9(数据处理)
  • localStorage 的容量限制和序列化开销 → M2(数据存储)
  • 数据去重策略:同一 key 保留最优记录 → M9(数据处理)

V2「要全公司一起比,不能只看自己浏览器的排名」

Section titled “V2「要全公司一起比,不能只看自己浏览器的排名」”

HR 说:“竞赛效果很好,下个月要全公司 500 人一起比!但这次每个人在自己电脑上答题,排行榜要是全公司共享的,不能只看自己浏览器里的。“你意识到 localStorage 是单机的,500 个人需要共享同一份排名数据。需要一个后端来接收所有人的分数并统一排序。先不上数据库,用 JSON 文件存就行,500 条数据而已。

搭建后端 API 统一管理排行榜数据,所有用户共享同一份排名。

在 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 秒刷新看到最新数据
  • 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 个排行榜请求。你需要彻底重构数据层。

用 Redis Sorted Set 替换 JSON 文件,实现高并发下的实时排行榜,并用 SSE 替代轮询。

在 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 channel
2. 后台 goroutine 批量消费 channel,每 5 秒或累积 100 条写入 PostgreSQL
3. 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
  • 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 结构远远不够了,你需要设计一套多维度排行榜架构。

设计多游戏、多时间维度的排行榜体系,使用 Redis Sorted Set 按维度隔离,支持自动过期和排行榜注册管理。

在 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}:alltime
2. 提交分数时同时写入三个维度:
- 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 倍
  • 管理仪表盘正确显示所有游戏排行榜概览
  • 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 连接断开后重连形成”惊群效应”。

优化 SSE 连接管理和消息分发机制,实现分数的批量写入,以及多实例部署下的实时推送方案。

在 V4 基础上解决万人并发下的连接和写入瓶颈,要求:
后端:Go + Gin + Redis + PostgreSQL + Nginx
SSE 连接优化:
1. 连接池化与分组 fan-out:
- SSE 连接按排行榜维度分组:sseGroup:{game_id}:{dimension}
- 后台 goroutine 每 3 秒计算一次排行榜变化,广播到对应分组
- 单个 goroutine 计算 → 广播给 N 个连接,而不是 N 个连接各自查 Redis
2. 连接数管理:
- 单实例最大 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_robin
2. 跨实例排行榜变化通知:
- 实例 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,写入性能提升
  • 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。更麻烦的是好友排名——每个用户的好友列表不同,意味着排名是动态的。同时反作弊也成了紧迫问题,有人用脚本刷分影响公平性。

设计组合维度排行榜(全球+好友+地区),在不为每个组合预建 Sorted Set 的情况下高效计算排名,同时引入反作弊分数验证管道。

在 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&region=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
  • 预计算 vs 懒计算:高频维度主动维护、低频维度按需计算 → M4(缓存策略)
  • 好友排行榜的设计权衡:不可能为每个用户预建 Sorted Set → M8(可扩展性)
  • Redis 分片策略:按业务维度分片 + 一致性 hash → M8(可扩展性)
  • 反向索引(followers)实现变更通知的高效查找 → M2(数据存储)
  • 流式验证管道(Kafka):多阶段校验解耦提交与上榜 → M5(消息与事件)
  • 反作弊的统计学方法:标准差检测异常值 → M9(数据处理)
  • 分布式系统中”全量预算”不可行时的降级策略 → M16(DevOps)
  • 多维度缓存的失效策略:事件驱动 + TTL 双重保障 → M4(缓存策略)
  • 全链路可观测性:从提交到上榜的每个环节都需要监控 → M15(可观测性)