Skip to content

Redis - Transactions

Redis transactions allow you to execute a block of commands as a single, atomic operation. This ensures that no other client can interrupt the execution of the command sequence.

  • Serial Execution: All commands within a transaction are serialized and executed sequentially. Once an EXEC command is triggered, Redis will not serve any other client’s requests until the transaction is complete.
  • Atomicity (with a caveat): Transactions are atomic. This means either all commands in the block are executed, or none are (in case of a syntax error before EXEC). However, Redis does not support rollbacks. If a command fails at runtime (e.g., trying to INCR a non-integer value), the transaction will continue, and all prior successful commands will remain committed.

A transaction is initiated with the MULTI command. After MULTI, the client can send multiple commands. These commands are not executed immediately but are queued by Redis. The transaction is finally triggered by the EXEC command.

# Start a transaction
127.0.0.1:6379> MULTI
OK
# Queue a command to set a user's name. Redis replies with QUEUED.
127.0.0.1:6379> SET user:1:name "Carol"
QUEUED
# Queue a command to increment the user's login count.
127.0.0.1:6379> INCR user:1:logins
QUEUED
# Execute the transaction. Redis runs both commands and returns their results.
127.0.0.1:6379> EXEC
1) OK
2) (integer) 1

WATCH provides a check-and-set (CAS) mechanism. If you WATCH a key, the transaction will only execute if that key has not been modified by another client since it was watched. If it was modified, the EXEC command will fail, returning a nil reply, and the client can retry the operation.

Imagine two clients trying to update a product’s inventory at the same time.

# Initial state: inventory is 10
127.0.0.1:6379> SET product:123:inventory 10
OK
# --- Client A starts ---
127.0.0.1:6379> WATCH product:123:inventory
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> DECRBY product:123:inventory 2
QUEUED
# --- Before Client A can EXEC, Client B quickly buys 1 item ---
# --- Client B's session (executes immediately) ---
# 127.0.0.1:6379> DECR product:123:inventory
# (integer) 9
# --- Client B is done ---
# --- Now, Client A tries to execute its transaction ---
127.0.0.1:6379> EXEC
(nil) # The transaction fails because the watched key was changed.
# Client A's application logic should now detect the nil reply,
# re-fetch the inventory, and retry the whole WATCH/MULTI/EXEC sequence.
CommandDescription
MULTIMarks the start of a transaction block. Subsequent commands are queued.
EXECExecutes all commands queued after MULTI.
DISCARDFlushes all commands queued after MULTI, exiting the transaction.
WATCH key [key …]Monitors given keys. If any are modified before EXEC, the transaction aborts.
UNWATCHFlushes all keys that are being watched.
  • No Runtime Rollback: Remember, if a command like INCR fails on a string value during EXEC, previous commands in the transaction (like a SET) are not rolled back. Your application must handle this possibility.
  • Syntax Errors: If a command has a syntax error (e.g., SETT instead of SET), modern Redis versions will abort the entire transaction upon EXEC.
  • Alternative: Lua Scripts: For complex atomic operations that require server-side logic, Redis Lua scripting is often a more powerful and flexible alternative to WATCH/MULTI/EXEC. A script is always executed atomically.