MULTI-&-EXEC
Redis transactions group commands to execute atomically. No other client can run commands in between. MULTI starts, EXEC runs, DISCARD cancels.
You'll Learn
- MULTI/EXEC transaction basics
- WATCH for optimistic locking
- DISCARD to cancel transactions
- When transactions vs Lua scripts
See Your Data, Not Terminal Text
Redimo visualizes every Redis data type beautifully. Edit inline, undo mistakes, stay safe in production.
1. What Are Redis Transactions?
Redis transactions guarantee that a group of commands executes as a single, uninterrupted sequence. No other client can run commands in between your transaction commands.
Note: Redis transactions are not like SQL transactions. There's no rollback if a command fails mid-transaction. All commands run, and you get individual results.
Basic Transaction
MULTI # Start transaction
OK
SET user:1001:name "John"
QUEUED # Command queued, not executed yet
INCR user:1001:visits
QUEUED
EXEC # Execute all queued commands
1) OK # Result of SET
2) (integer) 1 # Result of INCR2. MULTI: Start Transaction
MULTI marks the start of a transaction block. Subsequent commands are queued instead of executed immediately.
Transaction Flow
# Without transaction - commands execute immediately
SET balance:1001 100
SET balance:1002 50
# Another client could read inconsistent state here
# With transaction - all or nothing
MULTI
SET balance:1001 100
SET balance:1002 50
EXEC
# Both changes visible atomicallyQUEUED Response
QUEUED instead of its actual result. The real results come after EXEC.3. EXEC: Execute Transaction
EXEC runs all queued commands and returns an array of results, one per command.
Reading Results
MULTI
GET user:1001:name
GET user:1001:email
INCR user:1001:visits
EXEC
1) "John" # GET result
2) "john@example.com" # GET result
3) (integer) 42 # INCR resultImportant: No Rollback
If command #2 fails (e.g., wrong type error), commands #1 and #3 still execute. Redis returns the error in the results array, but doesn't undo anything.
4. DISCARD: Cancel Transaction
Changed your mind? DISCARD flushes all queued commands and exits transaction mode.
Canceling a Transaction
MULTI
SET balance:1001 0
SET balance:1002 0
# Wait, I don't want to do this!
DISCARD
OK
# Nothing happened, balances unchanged5. WATCH: Optimistic Locking
WATCH monitors keys for changes. If any watched key changes before EXEC, the transaction aborts (EXEC returns nil).
Check-and-Set Pattern
# Transfer $50 from account A to B, only if A has enough
WATCH balance:A
GET balance:A
"100"
# Now start transaction
MULTI
DECRBY balance:A 50
INCRBY balance:B 50
EXEC
# If another client modified balance:A between WATCH and EXEC:
# EXEC returns (nil) - transaction aborted
# You retry the whole operationWhy WATCH?
WATCH in Practice
# Increment if value < 100 (atomic check-and-set)
WATCH counter
val = GET counter
if val < 100:
MULTI
INCR counter
EXEC
else:
UNWATCH # Cancel watch without transaction
# If EXEC returns nil, another client changed counter
# Retry from WATCH6. Common Patterns
Atomic Transfer
# Move item from one set to another atomically
MULTI
SREM user:1001:cart item:5001
SADD user:1001:purchased item:5001
EXECBatch Update
# Update multiple fields atomically
MULTI
HSET user:1001 lastLogin 1705312245
HSET user:1001 loginCount 42
LPUSH user:1001:activity "login"
LTRIM user:1001:activity 0 99
EXECConditional Update with WATCH
def reserve_inventory(product_id, quantity):
while True:
redis.watch(f"inventory:{product_id}")
current = int(redis.get(f"inventory:{product_id}") or 0)
if current < quantity:
redis.unwatch()
return False
pipe = redis.pipeline(True)
pipe.decrby(f"inventory:{product_id}", quantity)
try:
pipe.execute()
return True
except WatchError:
continue # Retry7. Transactions vs Lua Scripts
Both provide atomicity, but they work differently:
Transactions (MULTI/EXEC)
- • Commands queued, executed in sequence
- • Can't use results mid-transaction
- • WATCH for optimistic locking
- • Client sends all commands, server executes
- • Good for simple atomic batches
Lua Scripts (EVAL)
- • Script runs atomically on server
- • Can use results within script
- • No need for WATCH (single-threaded)
- • Server executes logic
- • Good for complex conditional logic
When to Use What
# Transaction: Simple batch of known commands
MULTI
SET a 1
SET b 2
EXEC
# Lua: Need to read then write based on value
EVAL "
local val = redis.call('GET', KEYS[1])
if tonumber(val) > 10 then
return redis.call('INCR', KEYS[1])
end
return val
" 1 counter8. Error Handling
Understanding how errors work in transactions:
Syntax Errors (Pre-EXEC)
MULTI
SET key value
INCR # Missing key argument - syntax error
(error) ERR wrong number of arguments
SET other value
EXEC
(error) EXECABORT Transaction discarded because of previous errors
# Nothing executedRuntime Errors (During EXEC)
SET mykey "hello" # It's a string
MULTI
SET other "value"
INCR mykey # Can't INCR a string - runtime error
SET another "value"
EXEC
1) OK # SET succeeded
2) (error) ERR value is not an integer
3) OK # SET succeeded
# Partial execution - no rollback!No Rollback
9. CLI vs Redimo
CLI Challenges
- • Hard to verify transaction results visually
- • WATCH races require careful retry logic
- • No visual diff of before/after state
Redimo Benefits
- • See key values before and after operations
- • Inline editing respects data integrity
- • Visual confirmation of changes
- • Undo support for accidental changes
Quick Reference
| Command | Purpose |
|---|---|
MULTI | Start transaction block |
EXEC | Execute all queued commands |
DISCARD | Cancel transaction, flush queue |
WATCH key | Monitor key for changes |
UNWATCH | Stop watching all keys |
Start Using Transactions
Transactions ensure your multi-step operations don't interleave with other clients.
See your data changes clearly with Redimo.