Theory / 10 min
Transactions
Redis transactions are often misunderstood because the word "transaction" carries expectations from relational databases. Redis uses the word for a different, narrower idea: queue several commands and execute them as an uninterrupted batch.
The core shape is:
MULTI -> queue commands -> EXEC -> run queued commands in order -> return array of replies
There is no general rollback mechanism. There are no isolation levels to configure. The model is deliberately small.
Queuing Before Execution
After a client sends MULTI, most subsequent commands are not executed immediately. They are validated enough to be queued, and the server replies with QUEUED.
MULTI
SET a 1
INCR a
GET a
EXEC
When EXEC arrives, Redis runs the queued commands sequentially and returns an array containing each command's reply.
During the execution of that batch, Redis does not interleave commands from other clients. That gives the transaction its atomic execution property.
No Rollback Is A Design Choice
If a command inside EXEC returns a runtime error, Redis does not roll back earlier commands in the same transaction.
For example, a transaction might successfully set one key, then hit a wrong-type error on another command, then successfully run a later command. The EXEC reply array reports each outcome.
This sounds strange if you expect SQL-style transactions, but it matches Redis' command model. Redis commands are small, deterministic operations over in-memory structures. The transaction mechanism is primarily about ordered, uninterrupted execution, not undo logging.
Queue-Time Errors And Runtime Errors
Redis distinguishes between errors discovered while building the queue and errors discovered while executing it.
A malformed command with the wrong number of arguments can be rejected before EXEC. That can mark the transaction as dirty, causing EXEC to fail without running the queue.
A wrong-type operation may not be known until execution, because the key's type is part of the live database state. That error appears inside the EXEC reply array.
The distinction is subtle but important:
bad command shape -> transaction may be refused
bad runtime condition -> error becomes one result in the batch
WATCH Adds Optimistic Coordination
WATCH gives Redis transactions a compare-and-set flavor.
A client watches one or more keys, reads current values, then starts a transaction. If any watched key changes before EXEC, the transaction aborts.
WATCH balance
GET balance
MULTI
SET balance 90
EXEC
This is optimistic locking. Redis does not lock the key and block other clients. It observes whether the key changed and refuses to execute the queued batch if the watched premise is no longer true.
Internally, this requires the server to track watched keys per client and mark clients dirty when those keys are modified.
Transactions Live On The Client
Transaction state is per connection:
type TransactionState = {
active: boolean;
queue: Command[];
dirty: boolean;
watchedKeys: Set<string>;
};
That placement makes sense. One client is building one queued batch. Other clients continue issuing normal commands. The global database does not enter "transaction mode"; only the connection does.
The Redis Transaction Promise
Redis transactions promise a compact form of coordination:
commands are queued
EXEC runs them in order without interleaving
WATCH can abort if observed keys changed
runtime errors do not imply rollback
Once that promise is understood on its own terms, Redis transactions become less surprising and more useful. They are not a miniature relational database. They are a fast batching and optimistic coordination tool built for Redis' command execution model.
See what actually stuck.
Take the practice scenarios now.