Work with multiple keys
Work with multiple keys in Redis — practical steps, troubleshooting, and what to learn next.
Focus: work with multiple keys
You've mastered setting, getting, and deleting values one at a time. But what happens when your app needs to batch-check cache freshness, clear temporary sessions, or atomically move data between keys? Doing these one-by-one is slow, error-prone, and wastes roundtrips. Redis provides powerful multi-key commands that let you read, write, and delete many keys in a single call — cutting latency and keeping your logic clean.
The problem this lesson solves
Real applications rarely touch a single key in isolation. A typical session store might need to check the TTL of all active sessions, or a leaderboard might need to retrieve scores for the top 10 players. Without multi-key operations, you'd write a loop of GET or SET commands — each one a separate network roundtrip. This multiplies latency, increases connection overhead, and makes your code harder to read.
Multi-key commands solve this by bundling multiple operations into one atomic or pipeline-friendly call. They reduce network chatter, improve performance, and often provide atomicity guarantees that loops cannot.
Core concept / mental model
Think of Redis as a giant key-value dictionary on a remote server. Each single-key command is like asking a friend to fetch one item from a shelf. Multi-key commands are like handing your friend a list of items and letting them grab everything in one trip.
Definitions
- Multi-key commands: Redis operations that accept multiple key arguments in a single command (e.g.,
MGET,MSET,DEL,EXISTS). - Atomicity: A multi-key command either completes fully or not at all — no partial updates when using
MSET, for example. - Pipeline vs multi-key: Pipelining sends several commands together but doesn't guarantee atomicity; multi-key commands are inherently atomic for that single operation.
Pro tip: Multi-key commands are not available in Redis Cluster unless all keys hash to the same slot (same hash tag). For most single-instance or stand-alone Redis setups, they work perfectly.
How it works step by step
Let's walk through the core multi-key commands and how they behave.
1. MGET — Get multiple values in one call
Provide two or more keys, and Redis returns an array of values (nil for missing keys).
2. MSET — Set multiple key-value pairs atomically
All keys are written together; no partial success.
3. DEL — Delete multiple keys at once
Works the same as single-key DEL, but accepts a list.
4. EXISTS — Check existence of multiple keys
Returns the count of keys that exist.
5. KEYS vs SCAN — Pattern-based retrieval
KEYS patternreturns all matching keys in one shot (blocking on large datasets).SCAN cursor [MATCH pattern] [COUNT count]iterates incrementally, non-blocking — preferred for production.
Command syntax examples
# Atomic set of two keys
MSET user:101:name Alice user:101:role admin
# Get both values
MGET user:101:name user:101:role
# Returns: 1) "Alice", 2) "admin"
# Check existence of three keys
EXISTS user:101:name user:101:role user:999:none
# Returns: 2
# Delete two keys
DEL user:101:name user:101:role
# Returns: 2
Hands-on walkthrough
Let's build a practical example using Python's redis library. We'll simulate a user session store with multiple keys.
Setup
import redis
r = redis.Redis(host='localhost', port=6379, decode_responses=True)
r.flushdb() # clean slate
Example 1: Bulk insert with MSET
# Set three session keys for a user
r.mset({
"session:101:token": "abc123",
"session:101:expires": "2025-12-31",
"session:101:role": "editor"
})
print("MSET done")
Output:
MSET done
Example 2: Bulk read with MGET
keys = ["session:101:token", "session:101:expires", "session:101:role", "session:999:token"]
values = r.mget(keys)
print("Values:", values)
Output:
Values: ['abc123', '2025-12-31', 'editor', None]
Note: missing key session:999:token returns None.
Example 3: Check existence and delete multiple keys
to_check = ["session:101:token", "session:101:expires", "session:999:token"]
count = r.exists(*to_check)
print(f"Exist count: {count}")
# Delete first two
r.delete("session:101:token", "session:101:expires")
print(f"Remaining after delete: {r.exists('session:101:token', 'session:101:expires')}")
Output:
Exist count: 2
Remaining after delete: 0
Example 4: Using SCAN to find keys by pattern
# Add a few more keys
r.set("cache:homepage", "cached_html")
r.set("cache:about", "cached_about")
r.set("temp:123", "temporary")
# Non-blocking SCAN
cursor = 0
pattern = "cache:*"
found = []
while True:
cursor, keys = r.scan(cursor=cursor, match=pattern, count=10)
found.extend(keys)
if cursor == 0:
break
print(f"Keys matching 'cache:*': {found}")
Output:
Keys matching 'cache:*': ['cache:homepage', 'cache:about']
Compare options / when to choose what
| Command | Use case | Atomic? | Blocking? |
|---|---|---|---|
MGET / MSET |
Bulk read/write of known keys | Yes | No |
DEL |
Delete multiple keys | Yes | No |
EXISTS |
Check many keys for presence | Yes | No |
KEYS pattern |
List all keys matching a pattern (dev only) | No | Yes (blocking) |
SCAN |
Iterate keys matching a pattern (production) | No | No (non-blocking) |
When to choose what:
- Use
MGET/MSETwhen you know the exact keys and need atomic operations. - Use
EXISTSwith multiple keys to batch-check presence. - Use
DELto cleanup in bulk. - Use
SCANfor pattern-based iteration in production; avoidKEYSentirely on large datasets.
Troubleshooting & edge cases
Error: "MOVED" redirect in Cluster mode
If you're using Redis Cluster and keys hash to different slots, MGET and MSET will fail with a MOVED error. Solution: Use hash tags ({user:101}:token) to keep related keys in the same slot, or switch to a pipeline with per-slot grouping.
Memory: KEYS freezes Redis
KEYS * on a database with 10 million keys can block the server for seconds. Always prefer SCAN. If you must use KEYS, never run it in production.
Partial failures: MSET truly atomic
MSET is all-or-nothing. If one key fails (e.g., due to memory), none are written. MSETNX (set-if-not-exists) is also atomic but only succeeds if all keys are absent.
Missing keys return None (or nil)
MGET returns None for missing keys — not an error. Check for None in your application logic.
What you've learned & what's next
You can now:
- Use
MGETandMSETto read/write multiple keys atomically. - Use
EXISTSandDELwith multiple arguments to check and delete batches. - Differentiate between
KEYS(blocking, dev only) andSCAN(non-blocking, production-safe). - Handle edge cases like Cluster mode and large datasets.
Next up: Expire and TTL — learn how to set keys that automatically expire, and how to check remaining time-to-live. Perfect for cache invalidation and session cleanup.
Practice recap
Try this mini-exercise: write a Python script that uses MSET to store 3 configuration keys (theme, language, timeout) for a user. Then use MGET to read them back, print each, and delete two of them with DEL. Observe how EXISTS reflects the change.
Common mistakes
- Using
KEYS *in production — blocks Redis and can crash a server with millions of keys. UseSCANinstead. - Assuming
MGET/MSETwork in Redis Cluster without hash tags — keys must belong to the same slot or you'll get MOVED errors. - Not checking for
NoneinMGETresults — missing keys returnNone, not an exception. - Using
DELwithout verifying existence — safe but wasteful; if you need to check, useEXISTSfirst or pipeline.
Variations
- Using pipelines to batch multiple single-key commands when atomicity is not required but roundtrips matter.
- Using hash tags
{...}in Redis Cluster to force related keys into the same slot for multi-key operation support. - Using
MSETNXto set multiple keys only if none of them exist — atomic conditional set.
Real-world use cases
- Session store: bulk read/write session attributes (token, expiry, role) for a user in one roundtrip.
- Cache warmup: preload a dozen frequently accessed values into Redis atomically after a deployment.
- Cleanup cron job: batch delete all expired temporary keys matching a pattern using SCAN + DEL.
Key takeaways
MGETandMSETlet you read/write multiple keys atomically in one command — cutting network overhead.EXISTSwith multiple keys returns the count of existing keys;DELdeletes multiple keys at once.KEYSis blocking and dangerous for production — always useSCANfor pattern iteration.- Redis Cluster restricts multi-key commands to keys in the same slot; use hash tags to work around this.
- Missing keys in
MGETreturnNone— always handleNonein application code.
Keep learning
Related tutorials, quizzes, and articles for this topic.
More in this track
Quizzes
Articles
- Why Chasing 'Perfect Code' Is Actually Making Your Team Slower and More Fragile
- The Rise of Python Notebooks as Operational Tools: Why Jupyter Is Moving Beyond Data Science Into DevOps and Monitoring
- The Legacy Database Problem: Why Python Teams Are Migrating to SQLite and DuckDB for Production Workloads
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.