設計即時遊戲排行榜 (Gaming Leaderboard)

Medium★★★★Draft

FromSystem Design Interview

Concepts practiced🗄️ 鍵值儲存 (Key-Value Store)⚡ 快取策略 (Caching Strategies)🗄️ SQL vs NoSQL 選型

Convention: read the requirements & constraints first, sketch your own design, then expand the reference to compare.

📚 From the Books

設計即時遊戲排行榜 (Gaming Leaderboard)ProblemMediumRealtime

玩家贏一場加 1 分,即時顯示前 10 名與自己的名次。難點不在吞吐量,而在對數百萬持續變動的分數即時排名——關聯式資料庫的直接排序在這裡會崩潰。

📋 Requirements

🧠 Core Ideas

TIP

核心是「用對數時間就能排序的資料結構取代關聯式資料庫的全表排序」。Redis sorted set 讓每個元素在插入/更新時自動定位到正確順序,新增與查找皆為 O(log n)

分數必須在伺服器端設定:客戶端直接設分數易遭中間人攻擊,玩家可透過代理竄改。因此客戶端先呼叫遊戲服務確認勝利有效,再由遊戲服務呼叫排行榜服務更新分數。得分 API(POST /v1/scores)應為內部 API,只能由遊戲伺服器呼叫。

⚖️ Reference Design

為何關聯式資料庫直接排序不可行
  • 每月一張表存 user_idscore;加分用 UPDATE ... SET score=score+1
  • 找排名需 ORDER BY score DESC 全表排序,或用巢狀 COUNT(*) 算比自己高分的人數。
  • 資料到數百萬列時,排名操作要數十秒,達不到即時要求;且資料持續變動,連快取都不可行。加索引+LIMIT 只解決前 10 名,仍無法有效率地找出榜中特定使用者的排名。
Redis sorted set 解法
  • Redis 是記憶體內的鍵值儲存;sorted set 內部由 hash table(user→score)加 skip list(score→user)實作,故排序操作是 O(log n)
  • 加分:ZINCRBY leaderboard_feb_2021 1 'mary1934'(不存在則從 0 起算)。每月建新 sorted set,舊的移到歷史儲存。
  • 前 10 名:ZREVRANGE leaderboard_feb_2021 0 9 WITHSCORES(降序取最高分)。
  • 特定使用者排名:ZREVRANK。上下各 4 名:對排名 361 的玩家執行 ZREVRANGE ... 357 365
儲存、備份與擴展
  • 每筆 24 字元 ID + 2 bytes 分數 ≈ 26 bytes;2,500 萬 MAU 最壞情況約 650 MB,尖峰 2,500 QPS,單一 Redis 伺服器綽綽有餘。
  • 持久性顧慮:Redis 從磁碟重啟大型實例很慢,通常配唯讀副本,主實例失效時提升副本。
  • 另需 MySQL 的 user 與 point 兩張表(後者含時間戳):供其他功能使用,也可在 Redis 故障時逐筆 ZINCRBY 重建排行榜
  • 擴展到 5 億 DAU(65 GB、25 萬 QPS)需分片:偏好固定分區(依分數範圍切,可直接定位前 10 名與排名);雜湊分區(Redis cluster,CRC16(key)%16384)需 scatter-gather 且無法直接算特定排名。

🔑 Takeaways

✍️ My Notes

No notes yet — jot your takeaways or Q&A here.

📖 Further Reading