How to Iterate Redis Keys with SCAN in Python
Iterate all Redis keys matching a pattern using the SCAN command with a mock client to simulate pagination.
pip install redis
Python code
34 linesimport redis
def scan_keys(client, pattern="*", count=10):
keys = []
cursor = 0
while True:
cursor, batch = client.scan(cursor=cursor, match=pattern, count=count)
keys.extend(batch)
if cursor == 0:
break
return keys
if __name__ == "__main__":
# Mock Redis client to simulate SCAN iteration
class MockRedis:
def __init__(self):
self.data = {f"user:{i}" for i in range(100)}
self.data.update({f"post:{i}" for i in range(50)})
def scan(self, cursor=0, match="*", count=10):
keys = sorted(self.data)
total = len(keys)
start = cursor
end = min(start + count, total)
next_cursor = end if end < total else 0
batch = [k for k in keys[start:end] if k.startswith(match.replace("*", ""))]
return next_cursor, batch
mock = MockRedis()
user_keys = scan_keys(mock, pattern="user:*", count=20)
post_keys = scan_keys(mock, pattern="post:*", count=20)
print(f"Users: {len(user_keys)} keys")
print(f"Posts: {len(post_keys)} keys")
print(f"Total user keys sample: {sorted(user_keys)[:5]}")
Output
Users: 100 keys
Posts: 50 keys
Total user keys sample: ['user:0', 'user:1', 'user:10', 'user:11', 'user:12']
How it works
The client.scan method returns a cursor and a batch of keys; you loop until the cursor returns to 0. The count parameter is a hint for the batch size, not a strict limit. This avoids blocking the Redis server like KEYS does. The mock client replicates the cursor behavior with a simple list slice. Sorting keys ensures deterministic output in the mock, but real Redis order is undefined.
Common mistakes
- Assuming `count` limits total keys returned, not just per-call batch size
- Forgetting to handle the cursor loop, causing infinite loops or missing keys
- Using `KEYS` in production, which blocks the server for large key sets
Variations
- Use `scan_iter` method which abstracts the cursor loop internally
- Add `type` parameter to SCAN to filter by data type (e.g., 'hash')
Real-world use cases
- Cleaning up stale cache keys matching a prefix like 'session:*' in a maintenance script.
- Migrating a subset of keys from one Redis instance to another without blocking production traffic.
- Building a metrics dashboard that counts keys by pattern (e.g., 'user:*') across shards.
Sponsored
More from Caching & Redis
- Cache Asides in Python with a Read-Through Loader easy
- Cache Data in Redis with Python easy
- Cache Penetration Null Object Mock in Python medium
- Cache Stampede Prevention with SingleFlight in Python medium
- Cache Warming with Python: Preload Hot Keys easy
- Coalescing duplicate in-flight requests: one shared result for concurrent callers hard
Keep learning
Related tutorials and quizzes for this topic.