Overview
Source: Neo Kim — systemdesign.one
Real-time leaderboards are used in gaming, trading platforms, fitness apps, and hackathons. They must support millions of concurrent score updates while allowing instant rank queries — a challenging combination for any database.
Key Concepts
Sorted Set (Redis ZSET) — A Redis data structure that stores members with associated scores, automatically sorted. Supports O(log N) insertion/update and O(log N + K) rank queries. The core building block for real-time leaderboards.
Sharding — Distributing the leaderboard across multiple Redis nodes when the dataset exceeds single-node capacity.
Relative Rank — The position of a user among all participants (e.g., #1,423 out of 2M). Computing this exactly at scale requires counting all users with a higher score.
Functional Requirements
- Update a user's score
- Get a user's current rank
- Get the top N users (global leaderboard)
- Get users near a specific user's rank
- Support for millions of concurrent users
Architecture
- Score Update Service — Receives score update events (e.g., game completion). Writes to Redis ZSET and PostgreSQL.
- Redis ZSET — Stores
(userId, score)pairs sorted by score. Used for all rank queries.
- PostgreSQL — Durable store for score history and user profile data.
- Leaderboard API — Exposes endpoints for rank queries and top-N lists.
- Cache Layer — Caches top-100 leaderboard snapshot (refreshed every 60s for global leaderboards).
Core Redis Operations
ZADD leaderboard <score> <userId> # Insert/update score: O(log N) ZRANK leaderboard <userId> # Get rank (0-indexed): O(log N) ZREVRANK leaderboard <userId> # Get rank from top: O(log N) ZREVRANGE leaderboard 0 99 # Top 100 users: O(log N + K) ZRANGEBYSCORE ... # Users in score range: O(log N + K)
Handling Scale
Single Redis node handles up to ~50M users comfortably (sorted set in memory).
Beyond single node — Sharding strategies:
Strategy | Approach |
Score range sharding | Each shard owns a score range (e.g., 0–1000, 1001–2000) |
Consistent hashing | Users distributed across shards by userId hash |
Hierarchical | Local shards feed into a global aggregated leaderboard |
Approximate rank for ultra-scale: Skip list or count-min sketch approximation for systems with billions of users where exact rank is unnecessary.
Score Update Flow
- Game server publishes score event to Kafka
- Score Update Service consumes event
- Updates Redis ZSET with new score
- Writes score update to PostgreSQL for durability
- If top-100 cache is invalidated, refresh job runs
Key Trade-offs
Decision | Reasoning |
Redis ZSET over DB query | O(log N) vs. O(N) COUNT(*) query for rank |
Kafka for score events | Decouples game servers from leaderboard service; absorbs spikes |
Cached top-N | Global top-100 doesn't need real-time accuracy; 60s refresh acceptable |
PostgreSQL for history | Redis data is ephemeral; durable store needed for audit/replay |