# Why should I use a Sorted Set for a live leaderboard in Redis?

> Use a Sorted Set instead of plain string keys sorted in the app, since `ZADD` updates in O(log N) and `ZREVRANGE 0 99` returns the top 100 already ordered.

- Asked: 2026-06-07
- Answered: 2026-06-10
- Asked by: Sarp
- Tags: veritabani, redis, performans
- Source: https://www.muhammetsafak.com.tr/en/just-ask/redis-data-types-using-sorted-sets-for-a-leaderboard/
- Language: en-US
- Author: Muhammet Şafak

---
**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?


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)**.

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.

1. **O(log N) updates 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.
2. **Already-sorted top 100 with `ZREVRANGE`.** `ZREVRANGE leaderboard 0 99 WITHSCORES` returns the top 100 players **already sorted** in O(log N + 100). You do no sorting on the app side; even with millions of players this read is near-constant cost.
3. **One player's rank with `ZREVRANK`.** "What rank am I?" is answered by `ZREVRANK leaderboard <user>` in O(log N). With the string approach you'd have to fetch and count everyone; with a ZSET it's a single command.
4. **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.
5. **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.

## Related Reading

- [Should I use a partial index on a queue table where I only ever scan 'pending' rows?](https://www.muhammetsafak.com.tr/en/just-ask/should-i-use-a-partial-index-on-a-queue-table-where/) — Just Ask
- [Race condition on balance updates: Pessimistic Lock or Redlock?](https://www.muhammetsafak.com.tr/en/just-ask/preventing-race-conditions-on-balance-updates-pessimistic-vs-redlock/) — Just Ask
- [How do I make search-as-you-type fast with edge n-grams in Elasticsearch?](https://www.muhammetsafak.com.tr/en/just-ask/search-as-you-type-performance-with-edge-ngram-in-elasticsearch/) — Just Ask
