Why should I use a Sorted Set for a live leaderboard in Redis?
Question
I'm designing a live leaderboard (the top 100 highest-scoring players) for a gaming platform; millions of players' scores change in real time. I want to keep the data in Redis. Instead of storing each player's score in a plain String key (`user:123:score`) and pulling all users to sort them every time, what are the architectural advantages and the time-complexity (O(log N)) analysis of using Redis's Sorted Set (ZSET) data type?
Answer
Short answer: storing scores as plain user:123:score strings and sorting in the app is O(N log N) per read over millions of keys — and you re-fetch everything each time. The right tool is a Sorted Set (ZSET).
Short answer
The real point: the nature of a leaderboard is “sort and give me the top N.” If you try to do that work at read time, scale crushes you; you need to move the sorting to write time. And while you’re putting Redis in the middle of your hot path, it’s worth looking at the cache-side traps too — I covered stampedes in a separate answer.
Why
- The skip-list sorts as you write. The skip-list behind a ZSET keeps everything ordered as you write. So the cost is spread to write time, not read time; reads stay cheap and almost independent of player count.
- Read cost becomes independent of player count.
ZREVRANGE leaderboard 0 99 WITHSCORESreturns the top 100 players already sorted in O(log N + 100); even with millions of players this read is near-constant cost. - The string approach re-fetches everything for every question. Even a single question like “what rank am I?” means pulling and counting everyone; with a ZSET it’s one command.
What to do
- Update scores with
ZADD.ZADD leaderboard <score> <user>updates a member’s score in O(log N). When a player’s score changes it’s a single command; you don’t touch the whole table. The moment the score changes the set stays internally sorted. - Read the top 100 with
ZREVRANGE.ZREVRANGE leaderboard 0 99 WITHSCORESis all you need; you do no sorting on the app side. - Get one player’s rank with
ZREVRANK. “What rank am I?” is answered byZREVRANK leaderboard <user>in O(log N). - Window large sets. For huge sets, use periodic snapshots and time-windowed boards (separate daily/weekly keys). Watch ties: equal scores order lexicographically; if needed, encode a tiebreaker (e.g. a timestamp) into the score to make the order deterministic.
Bottom line: a ZSET is purpose-built for live leaderboards; use it instead of strings + app-side sorting. The moment you move sorting out of read time and into write time, the leaderboard runs instant and cheap even with millions of players.
Comments
Sign in with your GitHub account to join the discussion. Comments are stored in GitHub Discussions.