Redis Leaderboard with ZSET: Ranking, Pagination, Updates & Deletion
Sorted Sets (ZSETs) are Redis's secret weapon for leaderboards. They maintain elements sorted by score with O(log N) insertions and O(log N + M) range queries. This guide covers everything from basic ranking to advanced patterns.
The Problem
Building a leaderboard with a relational database means:
ORDER BY score DESCon every request — slow at scale.- Calculating rank requires counting rows —
O(N)per query. - Real-time updates require re-sorting.
Redis ZSET solves all three with built-in sorted data and O(log N) rank lookups.
Basic Leaderboard
Adding Scores
ZADD leaderboard 1500 "alice"
ZADD leaderboard 2300 "bob"
ZADD leaderboard 1800 "charlie"
ZADD leaderboard 2100 "diana"
ZADD leaderboard 1950 "eve"
Top N Players
ZREVRANGE leaderboard 0 2 WITHSCORES
Returns the top 3 players sorted by score (highest first).
Get a Player's Rank
ZREVRANK leaderboard "charlie"
ZSCORE leaderboard "charlie"
ZREVRANK returns 0-based rank (0 = first place). Charlie's rank and score in one go.
Score Updates
Increment Score
When a player earns points, use ZINCRBY:
ZINCRBY leaderboard 500 "charlie"
ZSCORE leaderboard "charlie"
ZREVRANK leaderboard "charlie"
Charlie's score goes from 1800 to 2300, and their rank updates automatically.
Replace Score
To set an absolute score (e.g., after recalculation):
ZADD leaderboard 3000 "alice"
ZSCORE leaderboard "alice"
ZREVRANK leaderboard "alice"
ZADD updates the score if the member already exists.
Pagination
For a leaderboard with thousands of players, you need pagination.
Page-Based Pagination
ZREVRANGE leaderboard 0 2 WITHSCORES
ZREVRANGE leaderboard 3 5 WITHSCORES
ZREVRANGE leaderboard 6 8 WITHSCORES
Page size = 3. Page 1 = indices 0–2, Page 2 = indices 3–5, etc.
Get Total Count
ZCARD leaderboard
Use this to calculate total pages: ceil(ZCARD / page_size).
Score-Based Pagination (Cursor)
For very large leaderboards, score-based pagination avoids offset overhead:
ZREVRANGEBYSCORE leaderboard +inf -inf LIMIT 0 3 WITHSCORES
Then use the last score as the cursor for the next page.
Removing Players
Remove a Single Player
ZREM leaderboard "eve"
ZREVRANGE leaderboard 0 -1 WITHSCORES
Remove by Rank Range
Remove the bottom 2 players:
ZREMRANGEBYRANK leaderboard 0 1
ZREVRANGE leaderboard 0 -1 WITHSCORES
Note: ZREMRANGEBYRANK uses ascending order (lowest score = rank 0).
Remove by Score Range
Remove all players with score below 1000:
ZREMRANGEBYSCORE leaderboard 0 999
Advanced Patterns
Time-Decayed Leaderboard
For a "trending" leaderboard where recent activity matters more:
ZADD trending 1739600100 "post:1"
ZADD trending 1739600200 "post:2"
ZADD trending 1739600050 "post:3"
ZREVRANGE trending 0 -1 WITHSCORES
Use timestamps as scores. Newer posts rank higher. Periodically prune old entries:
ZREMRANGEBYSCORE trending 0 1739500000
Multi-Criteria Ranking (Composite Score)
Redis ZSET scores are 64-bit floats. You can encode multiple criteria:
score = primary_score * 1000000 + (MAX_TIMESTAMP - timestamp)
This ranks by primary score first, then by time (earlier = higher) for tie-breaking.
ZADD composite 2300999900 "alice"
ZADD composite 2300999800 "bob"
ZADD composite 1800999950 "charlie"
ZREVRANGE composite 0 -1 WITHSCORES
Alice and Bob have the same primary score (2300), but Alice ranks higher because she achieved it earlier.
Weekly/Monthly Leaderboards
Use key naming with time periods:
ZADD leaderboard:2026:w07 500 "alice"
ZADD leaderboard:2026:w07 300 "bob"
ZINCRBY leaderboard:2026:w07 200 "alice"
EXPIRE leaderboard:2026:w07 604800
Set TTL to auto-cleanup old leaderboards.
Union for Aggregate Leaderboards
Combine weekly leaderboards into a monthly view:
ZADD week1 100 "alice" 200 "bob"
ZADD week2 150 "alice" 100 "bob"
ZUNIONSTORE monthly 2 week1 week2 AGGREGATE SUM
ZREVRANGE monthly 0 -1 WITHSCORES
Pitfalls
- Score precision: ZSET scores are IEEE 754 doubles. Integers up to 2^53 are exact. Beyond that, you lose precision.
- Large ZSETs: A ZSET with millions of members works fine, but
ZRANGE 0 -1on it will block Redis. Always paginate. - Tie-breaking: Without composite scores, players with the same score have undefined ordering. Redis sorts lexicographically for equal scores, which may not be what you want.
- Atomic updates: If you need to update score AND metadata, use a transaction or Lua script. Separate ZADD + HSET can leave inconsistent state.
Try It in the Editor
Head to the Redis Online Editor and build a leaderboard:
ZADD leaderboard 1500 "alice" 2300 "bob" 1800 "charlie" 2100 "diana" 1950 "eve"
ZREVRANGE leaderboard 0 -1 WITHSCORES
ZREVRANK leaderboard "charlie"
ZINCRBY leaderboard 500 "charlie"
ZREVRANGE leaderboard 0 2 WITHSCORES
ZREM leaderboard "eve"
ZCARD leaderboard
Watch how Charlie climbs the ranks after the score increment. That's real-time ranking with zero re-sorting overhead.