Table of Contents#
- Understanding the 'redis-cli keys *' Command
- Why 'keys *' Is Problematic
- 2.1 Blocking the Redis Event Loop
- 2.2 Performance Impact on Large Datasets
- 2.3 Scalability Issues
- The Better Alternative: The
SCANCommand- 3.1 How
SCANWorks - 3.2 Syntax and Key Options
- 3.1 How
- Step-by-Step Guide to Using
SCAN- 4.1 Prerequisites: Verify
redis-cliInstallation - 4.2 Basic
SCANUsage - 4.3 Listing All Keys (Loop Until Cursor Reaches 0)
- 4.4 Filtering Keys with
MATCH - 4.5 Formatting Output
- 4.1 Prerequisites: Verify
- Advanced Tips for
SCAN- 5.1 Using
SCANwith Patterns - 5.2 Counting Keys Safely
- 5.3 Integrating
SCANinto Scripts
- 5.1 Using
- Common Errors and Fixes
- Conclusion
- References
Understanding the 'redis-cli keys *' Command#
The redis-cli keys * command is often the first solution developers find when searching for "how to list all Redis keys." At first glance, it seems straightforward:
redis-cli keys * This command returns all keys in the current Redis database that match the pattern * (i.e., all keys). For example, if your database has keys like user:100, session:abc, and config:default, keys * will list them all.
Example Output:#
1) "user:100"
2) "session:abc"
3) "config:default"
Why 'keys *' Is Problematic#
While keys * works for small, development environments, it’s dangerous for production. Here’s why:
2.1 Blocking the Redis Event Loop#
Redis is single-threaded, meaning it processes one command at a time. The keys command scans the entire keyspace blocking all other operations until it completes. For a database with millions of keys, this can take seconds or even minutes—during which your application will experience timeouts or unavailability.
2.2 Performance Impact on Large Datasets#
The keys command has a time complexity of O(N), where N is the number of keys in the database. For 100,000 keys, it’s fast, but for 10 million keys, it becomes a bottleneck. This can trigger latency spikes, making keys unsuitable for production.
2.3 Scalability Issues#
As your Redis dataset grows, keys * becomes increasingly impractical. Cloud-hosted Redis services (e.g., AWS ElastiCache, Google Memorystore) often restrict or warn against using keys due to its impact on cluster stability.
The Better Alternative: The SCAN Command#
To safely list keys without blocking Redis, use the SCAN command. Introduced in Redis 2.8, SCAN is a non-blocking, cursor-based iterator that incrementally scans the keyspace.
3.1 How SCAN Works#
SCAN works by iterating over the keyspace in chunks:
- Start with a cursor value of
0(the initial scan). - Redis returns:
- A new cursor (to use for the next iteration).
- A subset of keys (up to a hinted count).
- Repeat the process with the new cursor until the cursor returns to
0(indicating the scan is complete).
This incremental approach ensures Redis can handle other commands between scans, avoiding blocking.
3.2 Syntax and Key Options#
The basic syntax for SCAN is:
redis-cli scan <cursor> [MATCH pattern] [COUNT count] <cursor>: The starting cursor (begin with0).MATCH pattern: Optional filter to return only keys matching a glob-style pattern (e.g.,user:*).COUNT count: Optional hint for how many keys to return per iteration (default: 10). Note:COUNTis not a guarantee—Redis may return more or fewer keys.
Step-by-Step Guide to Using SCAN#
Let’s walk through listing all keys safely with SCAN.
4.1 Prerequisites: Verify redis-cli Installation#
Ensure redis-cli is installed and accessible. Test with:
redis-cli --version If missing, install Redis (includes redis-cli) via your package manager (e.g., sudo apt install redis-server on Ubuntu).
4.2 Basic SCAN Usage#
Start with an initial scan using cursor 0:
redis-cli scan 0 Example Output:
1) "123" # Next cursor
2) 1) "user:100"
2) "session:abc"
3) "config:default"
Here, the cursor is 123, meaning we need to run SCAN again with 123 to continue.
4.3 Listing All Keys (Loop Until Cursor Reaches 0)#
To get all keys, loop until the cursor returns to 0. Use a bash loop for automation:
cursor=0
while [ $cursor -ne 0 ]; do
# Run SCAN and capture output
result=$(redis-cli scan $cursor)
# Extract the new cursor (first value)
cursor=$(echo "$result" | head -n1 | awk '{print $1}')
# Extract and print keys (skip the cursor line)
echo "$result" | tail -n +2
done How It Works:
- The loop starts with
cursor=0. - Each iteration runs
SCAN, updates the cursor, and prints keys. - The loop exits when
cursorbecomes0.
4.4 Filtering Keys with MATCH#
To list only keys matching a pattern (e.g., all user-related keys), use MATCH:
# List keys starting with "user:"
redis-cli scan 0 MATCH user:* Example Output:
1) "456"
2) 1) "user:100"
2) "user:101"
4.5 Formatting Output#
Use redis-cli flags to format output for readability or scripting:
-
--raw: Output keys without Redis’s bulk reply formatting (e.g., no quotes or line numbers).redis-cli scan 0 MATCH user:* --rawOutput:
456 user:100 user:101 -
--csv: Output in CSV format (useful for parsing).redis-cli scan 0 MATCH user:* --csvOutput:
"456","user:100","user:101" -
Save keys to a file:
cursor=0 while [ $cursor -ne 0 ]; do redis-cli scan $cursor --raw >> all_keys.txt cursor=$(redis-cli scan $cursor --raw | head -n1) done
Advanced Tips for SCAN#
5.1 Using SCAN with Patterns#
Combine MATCH with wildcards to refine results:
*: Match any character sequence (e.g.,user:*).?: Match a single character (e.g.,user:10?matchesuser:100,user:101).[]: Match a range (e.g.,user:[0-9]matchesuser:0touser:9).
5.2 Counting Keys Safely#
To count keys without keys *, use:
DBSIZE: Returns the total number of keys (O(1) time, non-blocking).redis-cli dbsizeSCAN+wc: Count keys matching a pattern (e.g.,user:*):cursor=0 count=0 while [ $cursor -ne 0 ]; do result=$(redis-cli scan $cursor MATCH user:* --raw) cursor=$(echo "$result" | head -n1) # Count keys (subtract 1 to exclude the cursor line) keys_in_batch=$(echo "$result" | tail -n +2 | wc -l) count=$((count + keys_in_batch)) done echo "Total user keys: $count"
5.3 Integrating SCAN into Scripts#
For automation, use SCAN in Python, Node.js, or other languages. Here’s a Python example with redis-py:
import redis
r = redis.Redis(host='localhost', port=6379, db=0)
cursor = 0
all_keys = []
while cursor != 0:
cursor, keys = r.scan(cursor=cursor, match='user:*', count=100)
all_keys.extend(keys)
print(f"Found {len(all_keys)} user keys: {all_keys}") Common Errors and Fixes#
"NOAUTH Authentication required"#
Cause: Redis server requires a password.
Fix: Add the -a flag with your password:
redis-cli -a your_password scan 0 Or authenticate after connecting:
redis-cli
127.0.0.1:6379> AUTH your_password
OK
127.0.0.1:6379> scan 0 "Could not connect to Redis at 127.0.0.1:6379: Connection refused"#
Cause: Redis server is not running, or the host/port is incorrect.
Fix:
- Start Redis:
sudo systemctl start redis-server. - Specify host/port explicitly:
redis-cli -h redis-host -p 6380 scan 0
Cursor Never Reaches 0#
Cause: SCAN may take multiple iterations for large datasets.
Fix: Be patient—continue looping until the cursor returns to 0.
Conclusion#
The keys * command is a quick fix but dangerous for production due to blocking and performance issues. The SCAN command provides a safe, non-blocking alternative by incrementally scanning the keyspace. By following the steps above, you can list all keys efficiently while keeping your Redis instance responsive.
Remember: Always test commands in staging before production, and use SCAN with MATCH and COUNT to tailor results to your needs.