Design a Real-time Leaderboard
Rank millions of players by score and show anyone their position instantly.
Asked at Amazon, Meta and 2 more
3 min read·8 sections
A real-time leaderboard ranks players by score: the top 10 in a mobile game, a weekly coding contest, a fitness challenge. Showing the top players is easy. The harder parts are telling any player their exact rank among millions instantly, and handling a steady stream of score updates.
Requirements#
Functional
- Update a player's score after each game.
- Show the top N players.
- Show any player's rank, and the players just above and below them.
- Weekly and all-time leaderboards; optionally per friends group or per country.
Non-functional
- Rank lookups in under 50 ms.
- Thousands of score updates per second during peak hours.
- Up to tens of millions of players on one board.
Why a database query struggles#
SELECT COUNT(*) + 1 FROM scores WHERE score > :myScore;This works for small tables, but with 50 million rows and constant updates each rank query scans a large index range. Running it for every player who opens the app doesn't scale.
Redis sorted sets#
A sorted set keeps members ordered by score using a skip list, so the main operations are O(log n):
ZINCRBY board:2026-w44 25 player:812 # add 25 points
ZREVRANK board:2026-w44 player:812 # 0-based rank, highest first
ZREVRANGE board:2026-w44 0 9 WITHSCORES # top 10
ZREVRANGE board:2026-w44 140 150 # neighbours around rank 145Fifty million members take a few gigabytes of memory, which fits on one large Redis node.
Architecture#
- Score service: validates updates (only trusted game servers can submit scores) and writes both to Redis and to a durable database.
- Leaderboard API: reads ranks from Redis and enriches player IDs with names and avatars from a profile cache.
- Durable store: the source of truth. If Redis is lost, the sorted sets are rebuilt from it.
Ties#
Players with equal scores need a stable order, usually "whoever got there first". Encode the time into the score, for example score × 10¹⁰ + (MAX_TS − timestamp), so earlier achievers rank higher among equals.
Time-bounded boards#
Use one key per period, such as board:2026-w44. A new week starts with a new key; old keys get a TTL or are archived to the database. No expensive reset is needed.
Scaling past one node#
- Read scaling: add Redis replicas for rank and top-N reads.
- Very large boards: shard players by score range or by hash. Top N is easy to merge from shards; exact global rank needs the count of higher scores in every shard, so sum
ZCOUNTacross shards. - Approximate ranks: for hundreds of millions of players, showing "top 2%" from a precomputed score histogram is often good enough and far cheaper.
Friends and country boards#
Small boards, such as a player's friends, can be computed on demand: fetch the friends' scores with one ZMSCORE call and sort in memory. Country boards are just more sorted sets, updated together with the global one.
Finished this walkthrough?
Mark it as read to track your progress in the list.