ZSCAN
Introduction
In Dragonfly, as well as in Redis and Valkey, the ZSCAN
command is used to incrementally iterate over the members of a sorted set along with their scores.
This is useful when working with large sorted sets to avoid blocking operations, allowing you to fetch elements in small chunks.
The ZSCAN
command provides a cursor-based interface to scan through a sorted set, ensuring that only a limited number of elements are returned with each call while redistributing work over multiple client requests.
Syntax
ZSCAN key cursor [MATCH pattern] [COUNT count]
- Time complexity: O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection.
- ACL categories: @read, @sortedset, @slow
Parameter Explanations
key
: The key of the sorted set to scan.cursor
: This is an opaque string used to iterate through elements. Initially, it must be0
, and it is updated by the server during each call.MATCH pattern
(optional): An optional glob-style pattern to filter the elements.COUNT count
(optional): WithCOUNT
, the user specifies the amount of work that should be done at every call in order to retrieve elements from the sorted set. This is just a hint to Dragonfly, and the actual number returned may be different.
Return Values
- The command returns an array of two elements:
- The first element is the new cursor to be used in the next scan. If the returned cursor is
0
, a full iteration is complete. - The second element is an array of member-score pairs from the sorted set for this iteration.
- The first element is the new cursor to be used in the next scan. If the returned cursor is
Code Examples
Basic Example
Scan members from a sorted set using the cursor, starting from 0
:
dragonfly$> ZADD myzset 10 "a" 20 "b" 30 "c"
(integer) 3
dragonfly$> ZSCAN myzset 0
1) "0" # New cursor (0 means full iteration has completed)
2) 1) "a" # Member
2) "10" # Score
3) "b" # Member
4) "20" # Score
5) "c" # Member
6) "30" # Score
Scanning with Pattern Matching
Only scan for members whose names match a certain pattern:
dragonfly$> ZADD myzset 10 "alpha" 20 "beta" 30 "gamma"
(integer) 3
# Scan members whose names start with "a".
dragonfly$> ZSCAN myzset 0 MATCH a*
1) "0"
2) 1) "alpha"
2) "10"
# Scan members whose names contain "a".
dragonfly$> ZSCAN myzset 0 MATCH *a*
1) "0"
2) 1) "alpha"
2) "10"
3) "beta"
4) "20"
5) "gamma"
6) "30"
Scanning Continuously Until Data Exhaustion
To fetch all members of a sorted set, the client has to keep scanning with the cursor provided from previous calls:
# Initialize cursor to 0.
CURSOR="0"
while true; do
# Fetch members from the sorted set.
SCAN_RESULT=$(redis-cli ZSCAN myzset $CURSOR COUNT 2)
# Update cursor from server response.
CURSOR=$(echo "$SCAN_RESULT" | head -n 1)
# Process members from the server response if needed.
echo $SCAN_RESULT
# Break the loop if the returned cursor is back to 0.
if [ "$CURSOR" -eq "0" ]; then
break
fi
done
Best Practices
- Since
ZSCAN
can return the same element more than once during an iteration (due to the nature of the scan mechanism), client-side deduplication may be required if complete accuracy is necessary. - Use the
MATCH
andCOUNT
options thoughtfully to optimize performance. Increasing theCOUNT
can reduce the total number of round trips but may return larger datasets to process.
Common Mistakes
- Misinterpreting the returned cursor value. When the returned cursor value is
0
, it indicates that the scan is complete. - Expecting precise control over the number of elements returned with
COUNT
. It is a hint to the Dragonfly server, but the actual result size may vary.
FAQs
Does ZSCAN
return duplicate elements?
The incremental nature of the ZSCAN
command implies that it may return the same element more than once over multiple invocations.
It is recommended to handle deduplication on the client-side if it's critical to avoid processing duplicate items.
Is ZSCAN
blocking?
No, ZSCAN
is non-blocking and designed for incrementally iterating through large sets.
It spreads work across multiple calls, preventing performance bottlenecks in a single blocking operation.
Can I use ZSCAN
to delete elements?
No, you cannot delete elements from a sorted set using ZSCAN
.
In the meantime, you should consider if your application logic allows you to delete or modify elements while scanning.
Modifying the sorted set while scanning may result in inconsistent results for your application logic.