You never know what you have to do.
What is a transaction
A transaction can execute multiple commands at a time. It is essentially a set of commands, with the following two important guarantees:
- A transaction is a separate isolation operation: all commands in the transaction are serialized and executed sequentially. During the execution of a transaction, it will not be interrupted by command requests sent by other clients.
- A transaction is an atomic operation: all commands in the transaction are either executed or not executed at all.
Why transactions
First, let's take a look at a case:
session-1 | session-2 |
---|---|
127.0.0.1:6379> set money 1000 OK |
127.0.0.1:6379> set money 1 OK |
127.0.0.1:6379> get money "1" |
127.0.0.1:6379> get money "1" |
The above is equivalent to two clients running at the same time. session-1 is client 1 and Session-2 is client 2. Client 1 sets money 1000 first, and then client 2 executes set money 1. When client 1 checks through get money, the final value becomes 1, that is, client 1 is interrupted by client 2 during execution. Is there any way to ensure that the whole operation of a client is a whole during execution? Yes, the following transactions and transaction related operation commands need to be understood.
Operation of transaction
MULTI
- Used to start a transaction. It always returns OK. After MULTI is executed, the client can continue to send any number of commands to the server. These commands will not be executed immediately, but will be placed in a queue. All incoming commands will return a status reply with the content of QUEUED. These QUEUED commands will be executed when the EXEC command is called.
127.0.0.1:6379> MULTI OK 127.0.0.1:6379> set age 22 QUEUED 127.0.0.1:6379> INCR age QUEUED 127.0.0.1:6379> get age QUEUED 127.0.0.1:6379>
EXEC
- When the command is called, all the commands in the queue will be executed. The reply is an array, and each element in the array is the reply generated by executing the command in the transaction. Among them, the order of reply elements is consistent with the order of command sending.
127.0.0.1:6379> MULTI OK 127.0.0.1:6379> set age 22 QUEUED 127.0.0.1:6379> INCR age QUEUED 127.0.0.1:6379> get age QUEUED 127.0.0.1:6379> EXEC 1) OK 2) (integer) 23 3) "23" 127.0.0.1:6379>
DISCARD
- When the command is called, the client can empty the transaction queue and give up executing the transaction. Cannot submit after.
127.0.0.1:6379> MULTI OK 127.0.0.1:6379> set age 22 QUEUED 127.0.0.1:6379> INCR age QUEUED 127.0.0.1:6379> get age QUEUED 127.0.0.1:6379> DISCARD OK 127.0.0.1:6379> EXEC (error) ERR EXEC without MULTI 127.0.0.1:6379>
Transaction workflow
Errors in transactions
When using transactions, you may encounter the following two kinds of errors:
- Before the transaction executes EXEC, the queued command may make an error. For example, commands may produce syntax errors (wrong number of parameters, wrong parameter names, etc.) or other more serious errors.
127.0.0.1:6379> set name ydongy QUEUED 127.0.0.1:6379> aaa name ydongy2 (error) ERR unknown command `aaa`, with args beginning with: `name`, `ydongy2`, 127.0.0.1:6379> EXEC (error) EXECABORT Transaction discarded because of previous errors. 127.0.0.1:6379>
We found that when a syntax error occurs in a transaction, exec finally prompts that the transaction does not exist, that is, when a syntax error is executed in the transaction, all commands in the overall transaction will not be executed. Include commands that have the correct syntax.
- Command may fail after EXEC call. For example, a command in a transaction may handle a key of the wrong type, such as using a list command on a string key, and so on.
127.0.0.1:6379> MULTI OK 127.0.0.1:6379> set name ydongy QUEUED 127.0.0.1:6379> get name QUEUED 127.0.0.1:6379> set age 22 QUEUED 127.0.0.1:6379> get age QUEUED 127.0.0.1:6379> lpush name 1 2 3 QUEUED 127.0.0.1:6379> get name QUEUED 127.0.0.1:6379> EXEC 1) OK 2) "ydongy" 3) OK 4) "22" 5) (error) WRONGTYPE Operation against a key holding the wrong kind of value 6) "ydongy" 127.0.0.1:6379>
In the above case, there is an error operation, that is, name is used as a list to append data. The syntax itself is not wrong, but the last instruction is not executed, but the correct instructions in the whole transaction are executed. It should be noted that the data corresponding to the executed commands will not be rolled back automatically, that is, Redis does not support rollback, and programmers need to implement rollback in their own code.
lock
Business scenario:
To solve the problem caused by this kind of concurrent operation, Redis' WATCH command can provide check and set (CAS) behavior for transactions, that is, optimistic locks. The keys that are watched will be monitored and it will be found whether these keys have been changed. If at least one monitored key is modified before EXEC is executed, the entire transaction will be canceled.
session-1 | session-2 |
---|---|
127.0.0.1:6379> set age 22 OK |
127.0.0.1:6379> get age "22" |
127.0.0.1:6379> WATCH age OK 127.0.0.1:6379> MULTI OK 127.0.0.1:6379> INCR age QUEUED |
|
127.0.0.1:6379> DECR age (integer) 21 |
|
127.0.0.1:6379> EXEC (nil) |
|
127.0.0.1:6379> get age "21" |
127.0.0.1:6379> get age "21" |
The nil returned by EXEC indicates that the transaction has failed. When EXEC is called, monitoring of all keys will be cancelled regardless of whether the transaction is successfully executed. In addition, when a client disconnects, the client's monitoring of keys will also be cancelled. If you need WATCH to monitor multiple keys, you can manually cancel the monitoring of all keys by using the UN WATCH command without parameters.