ZRANK and ZSCORE
Learn how to retrieve the rank of a member and its score from a sorted set using ZRANK and ZSCORE commands in Redis.
Focus: use zrank and zscore
Imagine you're running a leaderboard for an online game — thousands of players, scores changing every second. Without a way to ask "What's this player's current position?" or "What score do they have right now?", you'd have to sort through the entire dataset each time. In Redis, sorted sets solve this perfectly, and with ZRANK and ZSCORE you can instantly answer those two critical questions: a member's rank and their exact score.
The problem this lesson solves
Sorted sets are powerful — they maintain elements ordered by a numeric score, making leaderboards, priority queues, and real-time rankings easy to implement. But once you've added members to a sorted set (using ZADD), you often need to look up individual positions or scores without re-sorting. That's where ZRANK and ZSCORE come in: they provide O(log N) lookups for rank and score, but they behave differently depending on ordering direction, missing members, and edge cases. Without understanding these nuances, your application might return confusing results or even crash on nil values.
Core concept / mental model
Think of a sorted set as a double-entry directory: each member has a unique name (the element) and a file (the score). The set keeps members ordered by score, from lowest to highest.
- ZSCORE is like asking: "What is this person's file number?" — it returns the score associated with a given member.
- ZRANK is like asking: "Which position does this person occupy in line?" — it returns the zero-based index (rank) of that member, assuming ascending order (lowest score first).
There's also ZREVRANK, which reverses the order (highest score becomes rank 0). Both commands rely on the internal skip list data structure, giving them O(log N) performance.
Pro Tip:
ZRANKreturns a zero-based index. Rank 0 means the member has the lowest score. If you need the highest score first (like a leaderboard with winner at top), useZREVRANK.
How it works step by step
1. Adding data to a sorted set (prerequisite)
Before querying ranks and scores, you need a sorted set with some members. Use ZADD:
127.0.0.1:6379> ZADD leaderboard 100 "Alice" 200 "Bob" 150 "Charlie"
(integer) 3
This creates a sorted set named leaderboard with three members and their scores.
2. Using ZSCORE to retrieve a member's score
ZSCORE takes the key and member name, and returns the score as a floating-point number.
127.0.0.1:6379> ZSCORE leaderboard "Alice"
"100"
127.0.0.1:6379> ZSCORE leaderboard "Bob"
"200"
If the member doesn't exist, it returns (nil).
3. Using ZRANK to get the ascending rank
ZRANK returns the zero-based rank of the member when sorted by score from low to high.
127.0.0.1:6379> ZRANK leaderboard "Alice"
(integer) 0
127.0.0.1:6379> ZRANK leaderboard "Charlie"
(integer) 1
127.0.0.1:6379> ZRANK leaderboard "Bob"
(integer) 2
Alice has the lowest score (100) → rank 0. Bob has the highest (200) → rank 2.
4. Using ZREVRANK for descending order
If you want the highest score to be rank 0 (e.g., winner of a contest), use ZREVRANK:
127.0.0.1:6379> ZREVRANK leaderboard "Bob"
(integer) 0
127.0.0.1:6379> ZREVRANK leaderboard "Alice"
(integer) 2
Hands-on walkthrough
Let's run through a complete example in the Redis CLI. First, set up a leaderboard with varying scores:
127.0.0.1:6379> DEL leaderboard
(integer) 1
127.0.0.1:6379> ZADD leaderboard 50 "Emma" 75 "Liam" 100 "Olivia" 60 "Noah"
(integer) 4
Now practice retrieving rank and score:
127.0.0.1:6379> ZSCORE leaderboard "Liam"
"75"
127.0.0.1:6379> ZRANK leaderboard "Liam"
(integer) 2 # Because sorted order: Emma(50 rank0), Noah(60 rank1), Liam(75 rank2), Olivia(100 rank3)
127.0.0.1:6379> ZREVRANK leaderboard "Liam"
(integer) 1 # Because descending order: Olivia(rank0), Liam(rank1), Noah(rank2), Emma(rank3)
Working with ties
What if two members have the same score? Redis uses lexicographic ordering as a tiebreaker — the members are sorted alphabetically.
127.0.0.1:6379> ZADD leaderboard 100 "Zara" 100 "Adam"
(integer) 2
127.0.0.1:6379> ZRANK leaderboard "Adam"
(integer) 0 # Adam comes before Zara because 'A' < 'Z'
127.0.0.1:6379> ZRANK leaderboard "Zara"
(integer) 1
Using with Redis from Python
Here's how you'd use zrank and zscore in Python with the redis-py library:
import redis
r = redis.Redis(host='localhost', port=6379, decode_responses=True)
# Add some players
r.zadd('game_scores', {'Alice': 1200, 'Bob': 800, 'Charlie': 1500})
# Get Bob's score
score = r.zscore('game_scores', 'Bob')
print(f"Bob's score: {score}") # Output: 800
# Get Alice's rank (ascending)
rank = r.zrank('game_scores', 'Alice')
print(f"Alice's rank: {rank}") # Output: 1 (Bob is 0, Alice 1, Charlie 2)
# Get Charlie's rank in descending (best first)
rev_rank = r.zrevrank('game_scores', 'Charlie')
print(f"Charlie's reverse rank: {rev_rank}") # Output: 0
Compare options / when to choose what
| Command | Purpose | Returns | Order | Use case |
|---|---|---|---|---|
ZSCORE |
Get the score of a member | Float or nil | N/A | When you need the actual value (e.g., points, timestamps) |
ZRANK |
Get the position from low to high | Integer or nil | Ascending (lowest score first) | When you need a rank where smaller is better (e.g., race times) |
ZREVRANK |
Get the position from high to low | Integer or nil | Descending (highest score first) | Most leaderboards (highest score = 1st place) |
ZRANK WITHSCORE (Redis 7.2+) |
Get both rank and score in one call | Array | Ascending | When you need both in a single round trip |
Pro Tip: In Redis 7.2 and later,
ZRANKandZREVRANKaccept aWITHSCOREoption that returns both the rank and score as an array — reducing round trips.
Troubleshooting & edge cases
1. Member doesn't exist
127.0.0.1:6379> ZSCORE leaderboard "MissingUser"
(nil)
127.0.0.1:6379> ZRANK leaderboard "MissingUser"
(nil)
Always check for None/nil in your application code to avoid errors.
2. Empty sorted set
127.0.0.1:6379> ZSCORE empty_set "anything"
(nil)
127.0.0.1:6379> ZRANK empty_set "anything"
(nil)
3. Key doesn't exist at all
127.0.0.1:6379> ZSCORE nonexistent_key "member"
(nil)
Redis treats it the same as an empty sorted set — (nil).
4. Using wrong data type
127.0.0.1:6379> SET mystring "hello"
OK
127.0.0.1:6379> ZSCORE mystring "anything"
(error) WRONGTYPE Operation against a key holding the wrong kind of value
Always ensure the key holds a sorted set, not a string or other type.
5. Negative or floating scores
127.0.0.1:6379> ZADD scores -5.5 "negative" 3.1415 "pi"
(integer) 2
127.0.0.1:6379> ZSCORE scores "negative"
"-5.5"
127.0.0.1:6379> ZRANK scores "negative"
(integer) 0 # Negative scores are sorted first
6. Performance considerations
Both ZRANK and ZSCORE are O(log N) — fast even for millions of members. However, if you need to get the rank and score for many members in a loop, consider using ZRANK ... WITHSCORE (if on Redis ≥7.2) or batch with a Lua script to minimize round trips.
What you learned & what's next
You now know how to use ZRANK and ZSCORE to:
- Retrieve a member's score from a sorted set (ZSCORE)
- Get a member's rank in ascending or descending order (ZRANK / ZREVRANK)
- Handle common edge cases (missing members, wrong types, tie-breaking)
- Apply these commands in real-world leaderboard and scoring scenarios
Next step: In the following lesson, you'll learn about ZRANGE and ZREVRANGE — commands that retrieve ranges of members from sorted sets, allowing you to paginate through leaderboards or extract subsets of data. That's the natural companion to the positional queries you just mastered.
Key takeaway:
ZSCOREgives you the actual value;ZRANKgives you position. Together, they unlock efficient, O(log N) lookups on sorted sets — essential for any real-time ranking system.
Practice recap
Practice by creating a sorted set called high_scores with members Alice (score 850), Bob (1200), and Charlie (950). Use ZSCORE to get Charlie's score. Use ZRANK to find Alice's ascending rank. Then use ZREVRANK to see who is in first place. Add a new member with a score that ties Charlie, and observe how the rank changes. This solidifies your understanding of edge cases.
Common mistakes
- Forgetting that
ZRANKreturns a zero-based index — rank 0 means the first element, not the second. - Using
ZRANKwhen you need the highest score first — useZREVRANKinstead for descending leaderboards. - Not checking for
None/nilreturn when a member doesn't exist, causing aTypeErrorin Python code. - Applying
ZSCOREorZRANKon a key that holds a non-sorted-set type, leading to aWRONGTYPEerror. - Assuming member order is stable when scores are equal — ties are broken lexicographically by member name.
Variations
- Use
ZREVRANKwhen you need descending rank (highest score gets rank 0) — ideal for typical leaderboards. - In Redis 7.2+,
ZRANK key member WITHSCOREreturns both rank and score in a single round trip. - For batch queries, consider a Lua script to fetch multiple ranks/scores atomically instead of looping.
Real-world use cases
- Gaming leaderboard: get a player's current rank and score after each match to display on the dashboard.
- Real-time bidding system: retrieve the bid score of a participant and their rank among all bidders in an auction.
- Priority queue monitoring: check the priority (score) and position of a job in a processing queue to estimate wait time.
Key takeaways
ZSCOREreturns the numeric score of a member from a sorted set, ornilif the member doesn't exist.ZRANKreturns the zero-based rank in ascending order (lowest score first);ZREVRANKfor descending.- Both commands run in O(log N) time and require a sorted set key — wrong types produce
WRONGTYPEerrors. - Tied scores are ordered lexicographically by member name; plan accordingly when equal scores are possible.
- Always check for
nilreturns when the member might not exist to avoid crashes in application code.
Keep learning
Related tutorials, quizzes, and articles for this topic.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.