Transactions
MULTI, EXEC, DISCARD, WATCH
A transaction in Redis is a sequence of commands that are executed in order, as a single isolated operation.
- It ensures atomicity → either all commands execute or none do.
- Transactions in Redis use the commands:
MULTI→ Start a transactionEXEC→ Execute the transactionDISCARD→ Cancel the transactionWATCH→ Watch keys for changes (optimistic locking)
MULTI
- Marks the start of a transaction.
- After
MULTI, commands are queued, not executed immediately. - The queued commands will run only when
EXECis called.
MULTI
SET user:1 "Masum"
INCR counter
EXEC
MULTI→ starts transactionSETandINCRare queuedEXEC→ executes both in sequence- Atomicity ensures that either both run successfully or none run
EXEC
- Executes all commands queued after
MULTI. - If a command fails (like syntax error), only that command fails, but others run.
- The whole transaction is atomic from an isolation perspective → no other client’s command can interleave between them.
DISCARD
- Cancels a transaction.
- Clears all commands queued after
MULTI.
WATCH
- Provides optimistic locking.
- You can
WATCHone or more keys. - If any watched key is modified by another client before
EXEC, the transaction is aborted (returnsnil).
Useful for implementing check-and-set (CAS) logic.
Preventing Race Conditions with WATCH
Client A:
WATCH balance
val = GET balance # suppose balance = 100
MULTI
SET balance 90 # deduct 10
EXEC
If Client B modifies balance before Client A executes EXEC:
SET balance 50
Then Client A’s EXEC will fail:
(nil)
WATCHbalance → monitors thebalancekey.- Since another client modified it before
EXEC, Redis aborts the transaction. - This prevents race conditions (like double spending).
Transaction Flow Summary
WATCH key1 key2→ (optional) watch keys for changes.MULTI→ start transaction.Queuecommands (SET,INCR, etc.).EXEC→ execute transaction if watched keys unchanged.- If watched keys were modified → transaction aborts.
DISCARD→ cancel transaction manually if needed.
Optimistic Locking
- Optimistic locking is a technique to handle concurrent updates safely without using traditional locks.
- In Redis, it is implemented via the
WATCHcommand. - The idea: “I hope no one else changes this key while I’m working on it.”
- If the key changes before the transaction executes, Redis aborts the transaction to prevent race conditions.
This is particularly useful in high-concurrency environments like counters, wallets, or stock management.
How Optimistic Locking Works
-
WATCH keys → Redis monitors these keys.
-
Read the key(s) → get their current values.
-
MULTI → start a transaction and queue commands.
-
EXEC → attempt to execute transaction.
- If any watched key changed since
WATCH, transaction fails (returnsnil). - Otherwise, all queued commands execute atomically.
- If any watched key changed since
Key point: Optimistic locking is non-blocking. Other clients can still read/write the key; your transaction simply aborts if conflict occurs.
Bank Account Transfer
Imagine we want to deduct $10 from Alice’s balance safely, even if multiple clients try to modify it.
- Setup:
SET balance:alice 100 - Safe Deduction Using WATCH
WATCH balance:alice # Monitor the key
val = GET balance:alice # Suppose val = 100
if val >= 10:
MULTI # Start transaction
DECRBY balance:alice 10 # Deduct 10
EXEC # Attempt to execute transaction
Scenario 1: No other client modifies balance
EXECsucceeds → balance becomes 90.
Scenario 2: Another client modifies balance before EXEC
SET balance:alice 50
EXECfails → returns (nil)- You can retry the transaction if needed.
Ensuring atomic operations
- Atomic operations are operations that are completed entirely or not at all, with no interference from other clients.
- In Redis, atomicity is built into many commands by default.
- For example, commands like
INCR,DECR,SETNX,HSET, and list operations likeLPUSHare atomic. - This means you don’t need a transaction for single operations, even in a concurrent environment.
How Redis Ensures Atomicity
-
Single-threaded execution:
- Redis runs commands one at a time on a single main thread. This ensures no two commands from different clients can interleave.
-
MULTI/EXEC transactions:
- For multi-command operations, you can use
MULTI→EXEC. - Redis executes all queued commands atomically after
EXEC.
- For multi-command operations, you can use
-
Optimistic locking (
WATCH):- Ensures atomicity when you need to check a key’s value before modifying it.
Examples of Atomic Operations
-
Atomic INCR
SET counter 0
INCR counterEven if multiple clients run
INCR countersimultaneously, Redis ensures each increment is applied exactly once. -
Atomic Check-and-Set (SETNX)
SETNX lock "1"SETNX= "SET if Not eXists"- If the key doesn’t exist → sets it and returns
1 - If the key exists → does nothing and returns
0 - Useful for implementing distributed locks safely.
-
Atomic Multi-Key Operation with Transaction
Suppose you want to transfer money from Alice to Bob:
WATCH balance:alice balance:bob # Watch keys for changes
alice_balance = GET balance:alice
if alice_balance >= 10:
MULTI
DECRBY balance:alice 10
INCRBY balance:bob 10
EXEC
else:
DISCARD
MULTI→ queue multiple commandsEXEC→ executes all commands atomicallyWATCH→ ensures no one else modifies balances during this transaction
-
Atomic List Operation
LPUSH queue "task1"Even if multiple clients push to the same list simultaneously, Redis ensures each push is atomic.
Tips to Ensure Atomicity in Redis
- Use single commands whenever possible → they are already atomic.
- For multiple commands on related keys, use
MULTI/EXECto group them. - Use
WATCHif you need to implement conditional updates safely. - Avoid long-running scripts or operations inside transactions → can block other clients.