Redis Transactions
A Redis transaction lets you queue several commands and execute them all at once, in order, without any other client interrupting. This guarantees that no command from another connection slips in between your queued commands while they run.
The Bank Transfer Analogy
Moving money between two accounts requires two steps: deduct from Account A, add to Account B. If the server crashes after step one, Account A loses money without Account B gaining it. A transaction wraps both steps so they either both succeed or neither runs.
Without a transaction (dangerous): Step 1: DECRBY account:A 500 ← runs OK (server crash happens here) Step 2: INCRBY account:B 500 ← NEVER RUNS Result: 500 units vanished from A and never reached B. With a transaction (safe): MULTI DECRBY account:A 500 INCRBY account:B 500 EXEC ← both run together or neither runs Result: Both commands run as one unit.
The Four Transaction Commands
MULTI ← start a transaction (begin queuing) EXEC ← execute all queued commands DISCARD ← cancel the transaction (clear the queue) WATCH ← monitor a key; abort EXEC if it changes before EXEC runs
Step-by-Step Transaction Example
127.0.0.1:6379> MULTI OK ← queuing mode started 127.0.0.1:6379> SET msg "hello" QUEUED ← not executed yet, just queued 127.0.0.1:6379> INCR counter QUEUED 127.0.0.1:6379> SET status "active" QUEUED 127.0.0.1:6379> EXEC 1) OK ← result of SET msg 2) (integer) 1 ← result of INCR counter 3) OK ← result of SET status All three ran in sequence, uninterrupted.
Cancelling a Transaction
127.0.0.1:6379> MULTI OK 127.0.0.1:6379> SET promo "DEAL50" QUEUED 127.0.0.1:6379> DISCARD OK ← queue cleared, nothing executed
Error Handling Inside Transactions
Redis distinguishes two kinds of errors inside a transaction:
Error Type 1 – Syntax error before EXEC (command does not exist or has wrong syntax) MULTI SET name "Raj" BADCOMMAND arg ← unknown command EXEC ← entire transaction aborted → (error) EXECABORT Error Type 2 – Runtime error during EXEC (command exists but applied to wrong data type) SET counter "hello" ← counter is a string, not a number MULTI SET name "Raj" INCR counter ← will fail: "hello" is not a number SET city "Delhi" EXEC 1) OK ← SET name succeeded 2) (error) ERR value is not an integer ← INCR failed 3) OK ← SET city still succeeded Redis does NOT roll back on runtime errors. The other commands in the transaction still run.
This second behaviour surprises many developers. Redis does not provide full rollback like a relational database. If you need rollback-like safety, you must check for errors after EXEC and handle them manually.
WATCH – Optimistic Locking
WATCH lets you monitor one or more keys before a transaction. If any watched key changes between your WATCH call and your EXEC call, Redis aborts the transaction and returns nil instead of results. Your code then retries.
Scenario: Two processes both try to update the same inventory count.
Process A:
WATCH stock:item:77
current = GET stock:item:77 → "10"
MULTI
SET stock:item:77 "9" ← buy 1 item
EXEC
→ OK (nothing changed stock:item:77 before EXEC)
Meanwhile, Process B (runs between A's WATCH and A's EXEC):
SET stock:item:77 "8" ← bought 2 items
Now back to Process A's EXEC:
→ (nil) ← EXEC aborted! stock changed!
Process A sees nil and retries the whole sequence.
Flow diagram:
Process A: WATCH → GET → MULTI → queue → EXEC
│
Did watched key change? YES ───┴──▶ return (nil), retry
NO ─────────▶ execute normally
WATCH Retry Pattern
Loop:
WATCH stock:item:77
current = GET stock:item:77
new_value = current - 1
MULTI
SET stock:item:77 new_value
result = EXEC
if result == nil:
retry ← someone else changed the key; start again
else:
success
When to Use Transactions
Use MULTI/EXEC when: ✓ You update two or more related keys that must change together. ✓ You want to prevent any other client from interleaving commands. ✓ You build atomic operations without needing a full rollback. Use WATCH when: ✓ You read a value, compute something from it, and write back. ✓ You want to abort if someone else modifies the key first. ✓ You handle low-contention scenarios where retries are rare.
Key Points
- MULTI starts queuing; EXEC runs all queued commands at once without interruption.
- DISCARD clears the queue and exits transaction mode.
- Syntax errors before EXEC abort the entire transaction. Runtime errors during EXEC only fail the individual command — others still execute.
- Redis transactions do not roll back on runtime errors the way relational databases do.
- WATCH implements optimistic locking: if a watched key changes before EXEC, the transaction aborts and you retry.
