Requirements
A gaming leaderboard system must support:
- Update a player’s score in real-time (on match completion)
- Query a player’s current rank in O(log n)
- Retrieve top-K players with their scores and ranks
- Friend leaderboard – rank a player among their friends only
- Time-bucketed leaderboards – weekly, monthly, and all-time
- Support tens of millions of players and millions of score updates per day
Redis Sorted Set as the Core Data Structure
Redis sorted sets (ZSET) are the ideal primitive for leaderboards. Internally implemented as a skip list + hash map, they provide:
ZADD leaderboard {score} {player_id}– insert or update score – O(log n)ZREVRANK leaderboard {player_id}– 0-based rank from highest score – O(log n)ZRANK leaderboard {player_id}– rank from lowest score – O(log n)ZREVRANGE leaderboard 0 9 WITHSCORES– top 10 players – O(log n + k)ZRANGEBYSCORE– players within a score range – O(log n + k)ZMSCORE leaderboard player1 player2 ...)– batch score lookup – O(k)
A single Redis sorted set can hold 100M+ members and all operations remain fast due to the skip list structure.
Global Leaderboard
Implementing the global all-time leaderboard is straightforward with Redis:
- On score update:
ZADD leaderboard:global {new_score} {player_id} - Get player rank:
ZREVRANK leaderboard:global {player_id}(add 1 for 1-based rank) - Get top 10:
ZREVRANGE leaderboard:global 0 9 WITHSCORES - Get player score:
ZSCORE leaderboard:global {player_id}
Rank is 0-indexed in Redis – add 1 when displaying to users.
Score Update Strategies
Two common semantics for updating scores:
- High score (best performance): Only update if the new score is higher than the existing score. Use
ZADD leaderboard GT {new_score} {player_id}. The GT flag updates only if new score is greater than the current score. - Cumulative score (total points): Add to the existing score. Use
ZINCRBY leaderboard {delta} {player_id}. Common for “total XP earned” style leaderboards.
Pick the right semantic for your game type. A battle royale leaderboard typically uses high score; an RPG progression leaderboard uses cumulative.
Friend Leaderboard
A friend leaderboard ranks a player among their friends – typically a few hundred people at most.
Implementation:
- Store the friend graph in a relational DB or Redis set:
SMEMBERS friends:{player_id} - On query: fetch the friend list (typically < 500 members)
- Batch-fetch all friends’ scores:
ZMSCORE leaderboard:global friend1 friend2 ... - Sort client-side (or in app logic) and find the player’s position
This is O(friends) which is fast because friend counts are bounded. No need to maintain a separate sorted set per player. The tradeoff: slightly more latency (two round trips to Redis) vs. simplicity.
For very social games (>5000 friends), maintain a per-player friend leaderboard sorted set and update it on every score change of any friend – but this is rarely necessary.
Time-Bucketed Leaderboards
Weekly and monthly leaderboards require separate sorted sets per time bucket:
- Weekly key:
leaderboard:week:{year}:{week_number}– e.g.,leaderboard:week:2024:42 - Monthly key:
leaderboard:month:{year}:{month}– e.g.,leaderboard:month:2024:10 - All-time key:
leaderboard:global
On each score update, update all relevant buckets atomically using a Redis pipeline:
ZINCRBY leaderboard:global {delta} {player_id}ZINCRBY leaderboard:month:2024:10 {delta} {player_id}ZINCRBY leaderboard:week:2024:42 {delta} {player_id}
Set TTLs to control memory usage:
- Weekly keys: TTL of 90 days (keep ~12 weeks of history)
- Monthly keys: TTL of 2 years
- All-time: no TTL
Sharding for Scale
A single Redis sorted set can hold tens of millions of members before memory becomes a concern. If you need to scale beyond a single node:
- Shard by player_id % N: Each shard holds 1/N of the players.
- Score updates: Route to the correct shard based on player_id.
- Global top-K: Query top-K from each of the N shards, then merge the N sorted lists to find the true global top-K. This is O(N * K) per global top-K query – fine for small N and K=100.
- Global rank: To find the exact rank of a player, query each shard for “how many players scored more than X?” and sum the counts. This is O(N) Redis calls but can be pipelined.
For most games with under 50M players, a single Redis instance is sufficient. Sharding adds complexity – only introduce it when needed.
Persistence
Redis is an in-memory store. Plan for durability:
- Redis AOF (Append-Only File): Every write is logged to disk. Low data loss risk but higher disk I/O.
- Redis RDB snapshots: Periodic full snapshots. Simple but can lose up to snapshot_interval seconds of data on crash.
- DB sync: Periodically (e.g., every 5 minutes) snapshot the top-N players and full score data into a relational DB. This provides a recovery point and enables historical queries that Redis TTL would expire.
For leaderboards, losing a few seconds of score updates is usually acceptable. Use RDB with a 60-second interval as a pragmatic default.
Scale Numbers
Reference numbers for capacity planning:
- Redis sorted set: 100M+ members per set, ~100 bytes per member including overhead
- 100M players = ~10 GB of memory – fits on a single high-memory Redis instance
ZRANK/ZADD: O(log n) – sub-millisecond for 100M members- Throughput: 1M+ score update operations per second feasible with pipeline batching across multiple connections
- Top-10 query: sub-millisecond always (O(log n + 10))
Anti-Cheat Hooks
Leaderboards are high-value targets for cheating. Basic defenses:
- Rate limiting: Enforce a maximum number of score update events per player per second. A legitimate player cannot complete more than N matches per hour – anything above is suspicious.
- Score delta validation: Validate that the score delta for a single match falls within the range of what is achievable in the game. A player reporting +1,000,000 points from a single match where the max is 10,000 is clearly invalid.
- Server-authoritative scoring: Game servers – not clients – submit scores. Never trust score values that originate from the client.
- Statistical anomaly detection: Flag players whose score velocity (points per hour) is more than 3 standard deviations above the population mean for human review.
Twitch has real-time leaderboards for gaming competitions. See system design patterns for Twitch interview: gaming leaderboard and real-time ranking.
Uber uses ranking systems for driver ratings and incentives. See design patterns for Uber interview: real-time ranking and leaderboard design.
Snowflake interviews cover real-time ranking and analytics. See design patterns for Snowflake interview: leaderboard and ranking system design.
See also: Meta Interview Guide 2026: Facebook, Instagram, WhatsApp Engineering
See also: Snap Interview Guide