First, overview:
Like many other databases, Redis also provides a transaction mechanism as a NoSQL database. In Redis, the Multi/exec/discard/watch four commands are the cornerstone of our implementation of the transaction. I believe this concept is not unfamiliar to developers with relational database development experience, and even so, we will briefly list the implementation features of transactions in Redis:
1. All commands in the transaction will be serialized in the order that, during the execution of the transaction, Redis will no longer provide any services to other clients ' requests, thus ensuring that all the commands in the thing are executed by the atom.
2. In the case of a transaction in a Redis transaction, if a command fails, the subsequent command continues to execute.
3. We can open a transaction through the Multi command, and people with relational database development experience can interpret it as a "BEGIN TRANSACTION" statement. The commands that are executed after the statement are treated as actions within the transaction, and finally we can commit/rollback all operations within the transaction by executing the Exec/discard command. These two Redis commands can be considered equivalent to commit/rollback statements in a relational database.
4. Before a transaction is opened, if a communication failure occurs between the client and the server and causes the network to disconnect, all pending statements will not be executed by the server thereafter. However, if the network interrupt event occurs after the client executes the EXEC command, all the commands in the transaction are executed by the server.
5. When using append-only mode, Redis writes all writes within the transaction to disk in this call by calling system function write. However, if a system crash occurs during writing, such as downtime caused by a power failure, then perhaps only some of the data is written to disk and the other part of the data is lost. The Redis server performs a series of necessary consistency checks at reboot and, if it finds a similar problem, exits immediately and gives the appropriate error message. At this point, we need to take full advantage of the redis-check-aof tool provided in the Redis Toolkit, which can help us navigate to data inconsistency errors and roll back some of the data that has been written. After the repair, we can restart the Redis server again.
Ii. List of related commands:
Command prototypes |
Complexity of Time |
Command description |
return value |
MULTI |
|
Used to mark the start of a transaction, and subsequent commands are saved to the command queue until the exec is executed, and the commands are executed by the atom. |
Always return OK |
Exec |
|
Executes all the commands in a command queue within a transaction, while returning the current connection's state to its normal state, that is, a non transactional state. If the watch command is executed in a transaction, the EXEC command can execute all the commands in the transaction queue only if the keys monitored by watch are not modified, or exec discards all the commands in the current transaction. |
Atomicity returns the result of the return of the commands in the transaction. If watch is used in a transaction, EXEC returns NULL-MULTI-BULK reply once the transaction is discarded. |
Discard |
|
Rolls back all the commands in the transaction queue, and then restores the current connection's state to its normal state, that is, a non transactional state. If the watch command is used, the command will unwatch all keys. |
Always return OK. |
WATCHkey [key ...] |
O (1) |
Before the multi command is executed, you can specify the keys to be monitored, but before exec, exec discards all commands in the transaction queue if the monitored keys change. |
Always return OK. |
Unwatch |
O (1) |
Cancels the specified monitored keys in the current transaction, and if the exec or discard command is executed, it is not necessary to manually execute the command, because after that, all monitored keys in the transaction are automatically canceled. |
Always return OK. |
Third, the command example:
1. Transactions are performed normally:
Copy Code code as follows:
#在Shell命令行下执行Redis的客户端工具.
/> REDIS-CLI
#在当前连接上启动一个新的事务.
Redis 127.0.0.1:6379> Multi
Ok
#执行事务中的第一条命令, as you can see from the return result of this command, the command is not executed immediately, but is stored in the command queue of the transaction.
Redis 127.0.0.1:6379> incr T1
QUEUED
#又执行一个新的命令, as can be seen from the result, the command is also stored in the command queue of the transaction.
Redis 127.0.0.1:6379> incr T2
QUEUED
#执行事务命令队列中的所有命令, you can see from the results that the results of the commands in the queue are returned.
Redis 127.0.0.1:6379> exec
1) (integer) 1
2) (integer) 1
2. There are failed commands in the transaction:
Copy Code code as follows:
#开启一个新的事务.
Redis 127.0.0.1:6379> Multi
Ok
#设置键a的值为string类型的3.
Redis 127.0.0.1:6379> Set a 3
QUEUED
#从键a所关联的值的头部弹出元素, because the value is a string type and the Lpop command can only be used for the list type, the command will fail when the EXEC command is executed.
Redis 127.0.0.1:6379> Lpop A
QUEUED
#再次设置键a的值为字符串4.
Redis 127.0.0.1:6379> Set a 4
QUEUED
#获取键a的值 to confirm that the value was successfully set by the second set command in the transaction.
Redis 127.0.0.1:6379> Get a
QUEUED
#从结果中可以看出, the second command in the transaction fails Lpop execution, and the subsequent set and get commands are executed successfully, which is the most important difference between a Redis transaction and a transaction in a relational database.
Redis 127.0.0.1:6379> exec
1) OK
2) (Error) ERR Operation against a key holding the wrong kind of value
3) OK
4) "4"
3. ROLLBACK TRANSACTION:
Copy Code code as follows:
#为键t2设置一个事务执行前的值.
Redis 127.0.0.1:6379> set T2 TT
Ok
#开启一个事务.
Redis 127.0.0.1:6379> Multi
Ok
#在事务内为该键设置一个新值.
Redis 127.0.0.1:6379> Set T2 ttnew
QUEUED
#放弃事务.
Redis 127.0.0.1:6379> Discard
Ok
#查看键t2的值, you can see from the results that the value of the key is still the value before the transaction started.
Redis 127.0.0.1:6379> Get T2
"TT"
Iv. Watch commands and optimistic CAs-based locks:
In a redis transaction, the Watch command can be used to provide CAS (check-and-set) functionality. Suppose we monitor multiple keys before a transaction executes through the Watch command, and if the value of any key changes after watch, the transaction executed by the EXEC command is discarded and a null Multi-bulk reply is returned to notify the caller that the transaction failed to execute. For example, once again we assume that the Redis does not provide the INCR command to complete the atomic increment of the key value, if we want to implement this function, we can only write the appropriate code. Its pseudo code is as follows:
Copy Code code as follows:
val = Get MyKey
val = val + 1
SET MyKey $val
The above code guarantees that the execution results are correct only in the case of a single connection, because if multiple clients are executing the code simultaneously at the same time, a common error scenario--race contention (race condition)--occurs frequently in multithreaded programs. For example, both client A and B read the original value of MyKey at the same time, assuming that the value is 10, and then each of the two clients adds the value one after the set back to the Redis server, which results in a mykey result of 11 instead of the 12 we think. To solve a similar problem, we need the help of the watch command to see the following code:
Copy Code code as follows:
WATCH MyKey
val = Get MyKey
val = val + 1
MULTI
SET MyKey $val
Exec
Unlike the previous code, the new code monitors the key by using the Watch command before acquiring the value of the MyKey, then surrounds the set command in the transaction, which effectively guarantees that each connection before exec executes if the value of the MyKey being obtained by the current connection is modified by another connected client. The EXEC command for the current connection will fail to execute. This allows the caller to know whether the Val was reset successfully after judging the return value.