1: Basic introduction
1: Basic characteristics of transactions
① Atomicity(atomicity):
A transaction is an atomic operation consisting of a series of actions. The atomicity of the transaction ensures that the action is either fully completed or completely inoperative
② uniformity(consistency):
Once all transaction actions are completed, the transaction is committed. Data and resources are in a consistent state that meets business rules
③ Isolation(isolation):
Many transactions may process the same data at the same time, so everything should be isolated from other transactions to prevent data corruption
④ persistence(durability):
Once the transaction is completed and committed, it is permanent to the data operation in the database. On the contrary, any system error should not affect its results.
Typically, the result of a transaction is written to persistent storage
2: Redis transaction command introduction
multi: Open transaction
exec: End the transaction and execute it in sequence according to the added intra transaction commands
discard: Cancel the transaction and abort the execution of all commands in the transaction block
If the transaction is started( multi),And wrote a few redis Statement, we don't want this transaction anymore, including the statements written before,
We can use discard Statement to close a transaction
watch: Monitor one or more key,If the transaction is before execution, this key(Or more key)If it is modified by other commands, the transaction is interrupted and no commands in the transaction will be executed.
(Add optimistic locks, like mysql Optimistic lock inside version)
unwatch: Cancel monitoring, so key(Cancel locking)
2: Redis transaction command usage
1: Turn on / off transactions multi and exec
Since version 1.2.0, redis has introduced MULTI and EXEC instructions. MULTI marks the beginning of a transaction. The subsequent commands will not be executed temporarily, but will be stored in the queue. After EXEC is executed, the commands in the queue will be executed in sequence
127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379> set name zhangsan
OK
127.0.0.1:6379> get name
"zhangsan"
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set name jack
QUEUED
127.0.0.1:6379(TX)> set address anhui
QUEUED
127.0.0.1:6379(TX)> exec
1) OK
2) OK
127.0.0.1:6379> get name
"jack"
127.0.0.1:6379> get address
"anhui"
Executing MULTI will always return OK, indicating that a transaction has started. The subsequent instructions will not be executed immediately, but return "QUEUED", which means that the command has been accepted by the server and temporarily saved. Finally, after entering EXEC command, all commands in this transaction will be executed in turn. You can see that the server returned two OK at one time, The results returned here correspond to the commands sent in order, which indicates that all the commands in this transaction have been executed successfully
2: Cancel transaction discard
Since version 2.0.0, redis has introduced the DISCARD command, which is used to refresh all the previously queued commands in the transaction and restore the connection state to normal. It is to clear all the instructions in the queue before, and then end the transaction directly
127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379> set name zhangsan
OK
127.0.0.1:6379> get name
"zhangsan"
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set name jack
QUEUED
127.0.0.1:6379(TX)> discard -- implement discard Then it returns to normal state (so it will be executed later) exec (error will be reported)
OK
127.0.0.1:6379> get name
"zhangsan"
127.0.0.1:6379> exec
(error) ERR EXEC without -- MULTI No MULTI Error of EXEC
127.0.0.1:6379>
It can be seen that when we execute the EXEC command, we make a mistake: we execute the EXEC command without MULTI. Represents that there are no transactions in the current environment, which is what DISCARD does
3: Monitor watch and unwatch
watch is an important command in Redis transactions. You can use this command to monitor one or more keys. The monitored key is similar to an optimistic lock. Once the monitored key is updated or deleted, the rollback mechanism will be triggered
Redis official: watch is used to provide a check and set (CAS) behavior to redis transactions
Since version 2.2.0, redis has introduced WATCH and UNWATCH commands. The WATCH command can monitor one or more keys. Once one of the keys is modified (or deleted), the subsequent transaction will not be executed, and the monitoring will continue until the EXEC command (the command in the transaction is executed after EXEC, and the monitored key will be automatically unwatched after the EXEC command is executed). The function of UNWATCH is to cancel the monitoring of multiple keys by the WATCH command, and all monitoring locks will be cancelled.
Optimistic lock:
Just like his name, he will not think that the data will not go wrong, and he will not lock the data, but in order to ensure the consistency of the data, he will add one after each record
Tag (similar to version number), assuming A obtain K1 This mark, got k1 The version number of is 1, and it is modified at this time B Also got k1 This data,
of course, B The version number obtained is also 1, which is also correct k1 Modify, at this time, if B Submitted first, then k1 The version number of will be changed to 2. At this time, if A
When submitting data, he will find that his version number is inconsistent with the latest version number. At this time A Your submission will not succeed, A Our approach is to regain the latest information k1 Data,
Repeatedly modify data and submit data.
Pessimistic lock:
This mode will assume that the data will be wrong, so her approach is to lock the whole table, which will have strong consistency, but at the same time, it will have very low concurrency
(Common words database backup work, similar to table lock)
Case A: use watch after the transaction starts
127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379> set name zhansgan
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> watch name
(error) ERR WATCH inside MULTI is not allowed
127.0.0.1:6379(TX)> set name jack
QUEUED
127.0.0.1:6379(TX)> exec
1) OK
127.0.0.1:6379> get name
"jack"
As can be seen from the above example, Redis does not allow the use of the WATCH command within the transaction, and an error will be reported. However, even if WATCH is used, the transaction will not be aborted because of this error, and the transaction will be executed as usual
Case B: under the same client, change the monitored key after WATCH and before MULTI
127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379> watch name
OK
127.0.0.1:6379> set name zhangsan
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set address anhui
QUEUED
127.0.0.1:6379(TX)> set name jack
QUEUED
127.0.0.1:6379(TX)> exec
(nil) -- It means that the transaction has been rolled back. It is monitored key Changed
127.0.0.1:6379> get name
"zhangsan"
127.0.0.1:6379> get address
(nil)
It can be seen that I monitored the key with "name" before starting the transaction, and changed the key with "name" after starting the transaction; This will result in rollback, so all operations in the transaction will return to the original state before the transaction
Case C: under different clients, one client monitors a key to enter the transaction, and then the other client changes the value of this key (the sequence number is the order of command execution)
Since the function of the WATCH command is only to cancel the transaction after the monitored key is modified, it does not guarantee that other clients will not modify the monitored value. Therefore, when the EXEC command fails, the whole transaction needs to be manually re executed
Cancel monitoring:
127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379> watch name -- Turn on monitoring
OK
127.0.0.1:6379> set name zhangsan
OK
127.0.0.1:6379> unwatch -- Cancel all monitoring
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set name jack
QUEUED
127.0.0.1:6379(TX)> exec
1) OK
127.0.0.1:6379> get name
"jack"
3: Redis transaction error handling
Two types of command errors may be encountered during a transaction:
1: A command may fail to queue, so it is called EXEC Errors may have occurred before. For example, the command may be syntactically incorrect (wrong number of parameters, wrong command name,...),
Or there may be critical conditions such as low memory conditions if the server is configured to use maxmemory Instruction (with memory limit)
2: call EXEC The post command may fail, for example, because we performed an operation on a key with an incorrect value (for example, calling a list operation on a string value)
1: Team entry error
If syntax errors are encountered, all operations will be rolled back
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set name zhangsan -- Listed successfully
QUEUED
127.0.0.1:6379(TX)> set address -- Queue syntax error
(error) ERR wrong number of arguments for 'set' command
127.0.0.1:6379(TX)> set age 22 -- Listed successfully
QUEUED
127.0.0.1:6379(TX)> exec
(error) EXECABORT Transaction discarded because of previous errors.
-- Due to the previous error, EXECABORT Transaction discarded (rollback transaction)
2: Runtime error
The whole transaction will not be rolled back. After the failed command is executed, an error will be thrown and the command in the queue will continue to be executed
127.0.0.1:6379> multi OK 127.0.0.1:6379(TX)> set name zhangsan QUEUED 127.0.0.1:6379(TX)> set age 22 QUEUED 127.0.0.1:6379(TX)> incr name -- There will be problems when running, and the string data cannot be modified+1 QUEUED 127.0.0.1:6379(TX)> incr age QUEUED 127.0.0.1:6379(TX)> exec 1) OK 2) OK 3) (error) ERR value is not an integer or out of range 4) (integer) 23 127.0.0.1:6379>
4: Why does Redis not support rollback
Official introduction:
If you have a relational databases background, the fact that Redis commands can fail during a transaction, but still Redis will execute the rest of the transaction instead of rolling back, may look odd to you. However there are good opinions for this behavior:
-
Redis commands can fail only if called with a wrong syntax (and the problem is not detectable during the command queueing), or against keys holding the wrong data type: this means that in practical terms a failing command is the result of a programming errors, and a kind of error that is very likely to be detected during development, and not in production.
-
Redis is internally simplified and faster because it does not need the ability to roll back.
An argument against Redis point of view is that bugs happen, however it should be noted that in general the roll back does not save you from programming errors. For instance if a query increments a key by 2 instead of 1, or increments the wrong key, there is no way for a rollback mechanism to help. Given that no one can save the programmer from his or her errors, and that the kind of errors required for a Redis command to fail are unlikely to enter in production, we selected the simpler and faster approach of not supporting roll backs on errors.
Generally speaking, the author does not support transaction rollback for the following two reasons:
①: He thinks Redis During transaction execution, errors are usually caused by programming errors. Such errors usually occur only in the development environment, but rarely in the actual production environment,
So he didn't think it was necessary to Redis Develop transaction rollback function;
②: Transaction rollback is not supported because of this complex function and Redis The pursuit of simple and efficient design is not in line with the theme.
Transaction rollback is not supported here, which means that transaction rollback with runtime errors is not supported.
5: Spring boot operation Redis transaction
1: Incorrect usage
//The injection key values are String Object RedisTemplate object @Autowired private StringRedisTemplate stringRedisTemplate; // Error demonstration @Test void redisTemplateBase() { //Open transaction stringRedisTemplate.multi(); //obtain String Type of operation ValueOperations<String, String> opsForValue = stringRedisTemplate.opsForValue(); opsForValue.set("name","zhangsan"); opsForValue.set("age","22"); //Close the transaction and get the execution result of each command List<Object> exec = stringRedisTemplate.exec(); exec.forEach(System.out::println); } org.springframework.data.redis.RedisSystemException: Error in execution; nested exception is io.lettuce.core.RedisCommandExecutionException: ERR EXEC without MULTI
//It is said that when we execute the EXEC command, we do not find that the transaction MULIT is opened before
According to normal thinking, we need to use RedisTemplate to operate transactions. We will follow the above method, but eventually there will be errors;
2: Cause of error
The MULTI command was not executed before the exec command was executed. It's strange that we executed MULTI in the first sentence of the test method. By tracing MULTI, exec and other methods, we can see the following execution source code (spring data redis):
@Nullable public <T> T execute(RedisCallback<T> action, boolean exposeConnection, boolean pipeline) { Assert.isTrue(initialized, "template not initialized; call afterPropertiesSet() before using it"); Assert.notNull(action, "Callback object must not be null"); RedisConnectionFactory factory = getRequiredConnectionFactory();
// enableTransactionSupport indicates whether transaction support is enabled; The default is fasle RedisConnection conn = RedisConnectionUtils.getConnection(factory, enableTransactionSupport); ...... Omitted part }
//The injection key values are String Object RedisTemplate object @Autowired private StringRedisTemplate stringRedisTemplate; @Test void redisTemplateBase() { //Turn on transaction support stringRedisTemplate.setEnableTransactionSupport(true); //Open transaction stringRedisTemplate.multi(); //Add data by command operation stringRedisTemplate.opsForValue().set("address","anhui"); //End transaction List<Object> exec = stringRedisTemplate.exec(); System.out.println(exec); }
Most of them are solved online in this way, but I didn't study it carefully. It should be the old version. It can be like this; But next I will use SessionCallback to solve this problem
3: Start transaction and operate Redis
redisTemplate directly calls opfor To operate the redis database, you need to retrieve a connection every time you execute a command, so it consumes a lot of resources. The way to make a connection directly execute multiple statements is to use SessionCallback. RedisCallback is also used, but it is not commonly used. In this way, redis transactions can be used in the same connection
//The injection key values are String Object RedisTemplate object @Autowired private StringRedisTemplate stringRedisTemplate; @Test void redisTemplateBase() { // Recommended use SessionCallback Because it is encapsulated, and RedisCallback Not recommended;; Transactions can be executed internally Object dataA = stringRedisTemplate.execute(new SessionCallback<Object>() { @Override public <K, V> Object execute(RedisOperations<K, V> operations) throws DataAccessException { //hold RedisOperations Interface assignment to StringRedisTemplate(String K,V) StringRedisTemplate srt = (StringRedisTemplate) operations; //obtain String Type k,v operation ValueOperations<String, String> opsForValue = srt.opsForValue(); //Open transaction srt.multi(); opsForValue.set("name", "zhangsan"); opsForValue.set("salary", "40000.50"); opsForValue.get("name"); opsForValue.get("salary"); //End transaction and return return srt.exec(); } }); //Print the obtained value if (dataA instanceof List) { List<Object> data = (ArrayList<Object>) dataA; data.forEach(System.out::println); } }
.