Complete tutorial of Transactions in Redis
Redis transactions allow you to group multiple commands into a single atomic operation. An atomic operation means that all the commands within a transaction are executed together, ensuring that either all of them succeed or none of them do. Redis transactions use a concept called “MULTI/EXEC” to initiate and commit the transaction.
Important Topics for Transactions in Redis
- Syntax
- Commands
- Example
- What is a command queue and how to discard it?
- Implementation of optimistic locking using check and set
Syntax:
MULTI
<Redis commands>
EXEC
Commands:
1. MULTI
:
The MULTI command in Redis is used to start a transaction. Transactions in Redis allow you to group multiple commands together and ensure that they are executed atomically. This means that if any command within the transaction fails for any reason, all the commands are discarded, and nothing is executed. This ensures data integrity and consistency in Redis.
Syntax:
MULTI
How the MULTI command works?
- MULTI initiates a transaction in Redis.
- After the MULTI command is issued, all subsequent commands are queued for execution within the transaction.
- These commands are not executed immediately but are saved in a queue for later execution.
- The transaction remains open until you explicitly execute it using the EXEC command or discard it using the DISCARD command.
- While a transaction is in progress, Redis enters into a “queued” state, and commands are not executed in real-time.
- Instead, they are recorded and executed atomically when EXEC is called.
- Any command inside the transaction can fail due to errors, conflicts, or other reasons, and if that happens, none of the commands in the transaction are executed.
Example:
MULTI # Start a transaction
SET name “Alice” # Queue the SET command
HSET user:123 username “Alice” # Queue the HSET command
RPUSH queue1 “item1” # Queue the RPUSH commandExplanation:
- We start a transaction with MULTI.
- We queue multiple Redis commands (SET, HSET, and RPUSH) inside the transaction. These commands are not executed immediately but are saved for later execution.
- The transaction is now in progress, and Redis is in a “queued” state.
- To execute the queued commands and make them take effect, you need to use the EXEC command. If you want to cancel the transaction without executing any of the queued commands, you can use the DISCARD command.
2. EXEC:
The EXEC command in Redis is used to execute a group of commands that have been previously queued as part of a transaction using the MULTI command. It is a fundamental part of Redis transactions and ensures that all the queued commands are executed atomically, either all at once or none at all. Here’s a detailed explanation of the EXEC command:
Syntax:
EXEC
How the EXEC Command works?
- EXEC is the command that actually executes the queued commands within a transaction. When you issue the EXEC command, Redis processes all the queued commands in the order they were queued.
- If all the commands within the transaction execute successfully without any errors, Redis applies all the changes made by those commands, and the result of each command is returned as a list.
- If any of the commands within the transaction encounters an error during execution (e.g., a syntax error, a failed condition), Redis discards all the changes made by previous commands in the transaction, and none of the commands are applied.
- If a transaction is executed successfully, the EXEC command returns an array of results, where each element corresponds to the result of a command executed within the transaction. If a command doesn’t produce a value (e.g., SET), its result is nil in the array.
- If a transaction is discarded due to an error, the EXEC command returns nil.
Example:
MULTI # Start a transaction
SET name “Alice” # Queue the first command
HSET user:123 username “Alice” # Queue the second command
RPUSH queue1 “item1” # Queue the third command
EXEC # Execute the transactionExplanation:
- We start a transaction with MULTI and queue three commands.
- When we issue EXEC, Redis executes all three commands atomically.
- If any of these commands had failed due to an error, the entire transaction would have been discarded.
Error Handling: It’s important to note that Redis will queue all the commands inside a transaction even if some of them have errors. Errors are not raised until the EXEC command is issued. If any command encounters an error during execution, the entire transaction will be aborted, and none of the changes will be applied.
3. DISCARD Command:
The DISCARD command is used to cancel a transaction that is currently in progress. It discards all the commands that were queued in the transaction, and the Redis server will exit the transaction mode. If no transaction is in progress, DISCARD has no effect.
Syntax:
DISCARD
Example:
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> SET key1 “value1”
QUEUED
127.0.0.1:6379> SET key2 “value2”
QUEUED
127.0.0.1:6379> DISCARD
OK
In this example, the MULTI command starts a transaction, followed by two SET commands. However, before calling EXEC to execute the transaction, the DISCARD command is used, which cancels the transaction, and no changes are made to the keys.
4. UNWATCH Command:
The UNWATCH command is used to cancel the WATCH on all keys previously set using the WATCH command. It is commonly used in the context of optimistic locking in distributed systems.
Syntax:
UNWATCH
How UNWATCH command works?
- The UNWATCH command in Redis is used to cancel the effect of previously issued WATCH commands within the current client’s context.
- When a client uses WATCH to monitor one or more keys for modifications during a transaction, the client can later decide to cancel this monitoring using the UNWATCH command.
- The primary purpose of UNWATCH is to reset the watch state and allow the client to perform operations outside of the transaction context.
Behavior of UNWATCH:
- If you issue the UNWATCH command within a transaction (MULTI and EXEC context), it will reset the watch state for the keys being monitored. However, it doesn’t cancel the transaction itself. This means that even if you’ve used UNWATCH within a transaction, the transaction will still continue as usual, and you can proceed with other commands.
- If you issue the UNWATCH command outside of a transaction context (i.e., without a preceding MULTI command), it will have no effect. Redis will simply ignore the UNWATCH command
Example:
127.0.0.1:6379> WATCH key1 key2
OK
127.0.0.1:6379> SET key1 “value1”
OK
127.0.0.1:6379> UNWATCH
OK
In this example, the WATCH command sets a watch on two keys, key1 and key2. If any other client modifies these keys before a transaction is executed, the transaction will be aborted. The UNWATCH command cancels the watch on those keys.
5. WATCH Command:
The Redis WATCH command is used in conjunction with transactions to implement optimistic locking. It allows you to monitor one or more keys for changes. If any of the watched keys are modified by another client before the transaction is executed, the transaction will fail, and none of the commands within the transaction will be executed. This ensures that the transaction is only executed when the watched keys remain unchanged by other clients.
Syntax:
WATCH key [key …]
How WATCH command works?
- When you issue the WATCH command for a key, Redis remembers that you are watching this key and starts monitoring it.
- If another client modifies any of the watched keys before your transaction is executed, Redis detects the change and prevents your transaction from being executed.
- After the WATCH command is issued, Redis enters a “watching” state. All subsequent commands are queued, but they are not executed immediately. They will only be executed if the watched keys remain unchanged.
Behavior of WATCH command:
- If the watched key is modified before the EXEC command, the transaction is considered to be in conflict, and the EXEC command returns a null value.
- If none of the watched keys are modified, the transaction will be executed successfully when the EXEC command is issued.
- If a conflict occurs, you can handle it by retrying the transaction, updating the data, or notifying the user.
Example:
127.0.0.1:6379> WATCH key1 key2
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> SET key1 “value1”
QUEUED
127.0.0.1:6379> SET key2 “value2”
QUEUED
127.0.0.1:6379> EXEC
(nil)Explanation:
- The WATCH command sets a watch on key1 and key2.
- After that, a transaction is initiated using the MULTI command, and two SET commands are queued.
- However, if any other client modifies key1 or key2 before EXEC is called, the transaction will be aborted, and the EXEC will return nil.
Use Cases of WATCH command:
- WATCH is commonly used in scenarios where multiple clients need to perform operations on shared data while ensuring that only one client’s changes are applied at a time. This is often referred to as optimistic locking.
- It is also used in cases where you want to implement conditional transactions, ensuring that certain conditions are met before executing a transaction.
- WATCH can be part of more complex patterns for distributed locking and coordination.
Example:
Here we want to transfer money between two Redis keys representing the balances of two users:
127.0.0.1:6379> SET user1_balance 100;
OK
127.0.0.1:6379> SET user2_balance 50
OK
Now, let’s perform the transaction to transfer 20 units from “user1_balance” to “user2_balance”:
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> DECRBY user1_balance 20
QUEUED
127.0.0.1:6379> INCRBY user2_balance 20
QUEUED
127.0.0.1:6379> EXEC
1) (integer) 80 # Result of DECRBY operation on user1_balance
2) (integer) 70 # Result of INCRBY operation on user2_balance
Explanation:
- Here the
DECRBY
command reduces the balance of “user1_balance” by 20 - The
INCRBY
command increases the balance of “user2_balance” by 20. - The
MULTI
command starts the transaction - The
EXEC
command executes all the queued commands within the transaction - If any of the commands had failed, the entire transaction would have been aborted, and no changes would have been applied.
What is a command queue and how to discard it?
A command queue in the context of Redis transactions refers to a list of commands that have been queued up to be executed as part of a transaction. When you initiate a Redis transaction using the MULTI command, any subsequent commands are placed in this queue, awaiting the final execution using the EXEC command. This allows you to group multiple commands together and ensure that they are executed atomically, maintaining data consistency.
Below is the working of command queue in Redis transactions:
- MULTI Command: When you issue the MULTI command, Redis enters transaction mode. This means that any subsequent Redis commands you send will not be executed immediately. Instead, they will be added to the command queue, waiting for the EXEC command to be issued.
- Queuing Commands: After the MULTI command, each Redis command you send is added to the command queue. These commands are not executed individually but are recorded as part of the transaction.
- Transactional Nature: Redis transactions guarantee that all the commands in the queue are executed as a single, atomic operation. This means that either all the commands in the queue are executed successfully, or none of them are executed at all. If any command in the queue fails, Redis discards the entire transaction.
- EXEC Command: When you’re ready to execute the queued commands, you issue the EXEC command. This command executes all the commands in the queue sequentially, as part of a single transaction.
Discarding the Command Queue:
If something goes wrong before the EXEC command is issued and you decide not to proceed with the transaction, you can use the DISCARD command to discard the entire command queue. The DISCARD command resets Redis to its non-transactional state, and all the commands in the queue are discarded.
Syntax:
DISCARD
Example:
MULTI
SET key1 value1
SET key2 value2
DISCARD
Note: If you choose to discard the transaction using the DISCARD command, the changes made by the SET commands to key1 and key2 will not take effect, as the entire command queue is discarded.
Implementation of optimistic locking using check and set
Optimistic Locking and Check and Set (CAS):
Optimistic locking is a technique used to handle concurrent updates in a distributed system. The idea is to allow multiple clients to read data concurrently, and when they want to update the data, they first check if the data has been modified by any other client before proceeding with the update.
The Check and Set (CAS) pattern is a common method for implementing optimistic locking. It involves the following steps:
- The client reads the current value of the data it wants to update.
- The client calculates the new value and tries to set it if the current value matches the value it initially read.
- If the current value has changed since the client read it, the update is rejected, and the client needs to handle the conflict (retry or inform the user).
Implementing Optimistic Locking using WATCH and CAS in Redis:
In Redis, you can implement optimistic locking using the WATCH command along with a transaction. Here’s is the steps to implement optimistic locking using WATCH AND CAS in Redis:
- WATCH the Key: Before starting the transaction, use the WATCH command to monitor the key you’re going to update.
- Read the Current Value: Read the current value of the key. This is the value you’ll use to calculate the new value.
- Start a Transaction (MULTI-EXEC): Start a transaction block using the MULTI command.
- Calculate the New Value: Calculate the new value based on the current value and your business logic.
- Compare and Set (CAS): Use the SET command within the transaction to update the key’s value if and only if the value has not changed since you initially read it.
- Execute the Transaction (EXEC): Execute the transaction using the EXEC command.
- Handle Transaction Result: If the EXEC command returns a null response, it means that the data has been modified by another client between the WATCH and EXEC commands. Handle this case by either retrying the operation or informing the user.
Remember, optimistic locking using CAS is a powerful way to handle concurrency, but you should carefully design your application’s logic and error handling to ensure data consistency and proper user experience in case of conflicts. It’s important to note that Redis transactions do not support rollback like traditional relational database transactions. If an error occurs during the execution of a transaction, you will need to handle the error and revert any side effects manually.
Contact Us