Redis transaction introduction
summary
Redis adopts the optimistic locking method for transaction control. It uses the watch command to see the given key. When exec(t commits the transaction), if the monitored key is valid for the whole connection, such as disconnecting the connection, the supervisor and transaction will be automatically cleared. Of course, the exec, discard and unwatch commands will clear all monitoring in the connection
Basic instruction
multi open transaction
exec commit transaction
discard cancel transaction
watch monitoring. If the monitored value changes, the transaction submission will fail
unwatch cancel monitoring
Redis ensures that all commands in a transaction are executed or not executed (atomicity). If the client is disconnected before sending the EXEC command, redis will empty the transaction queue and all commands in the transaction will not be executed. Once the client sends the EXEC command, all commands will be executed. Even if the client is disconnected, it doesn't matter, because all commands to be executed have been recorded in redis.
Redis transaction control practice
exec commit transaction
Simulated transfer, tony 500, jack 200, tony to Jack 100.
127.0.0.1:6379> set tony 500 OK 127.0.0.1:6379> set jack 200 OK 127.0.0.1:6379> mget tony jack 1) "500" 2) "200" 127.0.0.1:6379> multi #Open transaction OK 127.0.0.1:6379(TX)> decrby tony 100 #All instruction operations are queued QUEUED 127.0.0.1:6379(TX)> incrby jack 100 QUEUED 127.0.0.1:6379(TX)> mget tony jack QUEUED 127.0.0.1:6379(TX)> exec #Commit transaction 1) (integer) 400 2) (integer) 300 3) 1) "400" 2) "300" 127.0.0.1:6379> mget tony jack 1) "400" 2) "300" 127.0.0.1:6379>
discard cancel transaction
Note that the redis transaction is too simple. Instead of rolling back, it has to be cancelled.
127.0.0.1:6379> mget tony jack 1) "400" 2) "300" 127.0.0.1:6379> multi OK 127.0.0.1:6379> incrby jack 100 QUEUED 127.0.0.1:6379> discard OK 127.0.0.1:6379> get jack "300" 127.0.0.1:6379> exec (error) ERR EXEC without MULTI
When an incorrect instruction occurs, the transaction will also be cancelled automatically.
127.0.0.1:6379> mget tony jack 1) "400" 2) "300" 127.0.0.1:6379> multi OK 127.0.0.1:6379(TX)> incrby jack 100 QUEUED 127.0.0.1:6379(TX)> abcd (error) ERR unknown command `abcd`, with args beginning with: 127.0.0.1:6379(TX)> get jack QUEUED 127.0.0.1:6379(TX)> exec (error) EXECABORT Transaction discarded because of previous errors. 127.0.0.1:6379> get jack "300" 127.0.0.1:6379>
Second kill ticket grabbing transaction
Based on a second kill and rush purchase case, demonstrate the optimistic lock mode of redis, for example
Step 1: open client 1 and perform the following operations
127.0.0.1:6379> set ticket 1 OK 127.0.0.1:6379> set money 0 OK 127.0.0.1:6379> watch ticket #Optimistic lock. Observe the value. If the value changes, the transaction fails OK 127.0.0.1:6379> multi #Open transaction OK 127.0.0.1:6379> decr ticket QUEUED 127.0.0.1:6379> incrby money 100 QUEUED
Step 2: open client 2 and perform the following operations. Before client 1 submits the transaction, client 2 buys the ticket
127.0.0.1:6379> get ticket "1" 127.0.0.1:6379> decr ticket (integer) 0
Step 3: return to client 1: commit the transaction and check the value of ticket
127.0.0.1:6379> exec (nil) #Execute transaction, failed 127.0.0.1:6379> get ticket "0" 127.0.0.1:6379> unwatch #Cancel monitoring
Jedis client transaction operations
Conduct transaction test based on Jedis, and the code is as follows:
package com.jt; import org.junit.Test; import redis.clients.jedis.Jedis; import redis.clients.jedis.Transaction; public class JedisTransactionTests { @Test public void testTransaction(){ Jedis jedis=new Jedis("192.168.126.130",6379); jedis.auth("123456"); jedis.set("tony","300"); jedis.set("jack","500"); //To implement the operation, tony transfers 100 to jack //Open transaction Transaction multi = jedis.multi(); //Perform business operations try { multi.decrBy("tony", 100); multi.incrBy("jack", 100); int n=100/0;//Simulation anomaly //Commit transaction multi.exec(); }catch(Exception e) { //An exception occurred to cancel the transaction multi.discard(); } String tonyMoney=jedis.get("tony"); String jackMoney=jedis.get("jack"); System.out.println("tonyMoney="+tonyMoney); System.out.println("jackMoney="+jackMoney); jedis.close(); } }
Jedis client second kill operation practice
package com.jt.demos; import redis.clients.jedis.Jedis; import redis.clients.jedis.Response; import redis.clients.jedis.Transaction; import java.util.List; /** * redis Second kill exercise: * Simulate that both threads rush to buy the same ticket (consider music lock) */ public class SecondKillDemo02 { public static void secKill(){ Jedis jedis=new Jedis("192.168.126.130",6379); jedis.auth("123456"); jedis.watch("ticket","money"); String ticket = jedis.get("ticket"); if(ticket==null||Integer.valueOf(ticket)==0) throw new RuntimeException("No inventory"); Transaction multi = jedis.multi(); try { multi.decr("ticket"); multi.incrBy("money", 100); List<Object> exec = multi.exec(); System.out.println(exec); }catch (Exception e){ e.printStackTrace(); multi.discard(); }finally { jedis.unwatch(); jedis.close(); } } public static void main(String[] args) { Jedis jedis=new Jedis("192.168.126.130",6379); jedis.auth("123456"); jedis.set("ticket","1"); jedis.set("money","0"); Thread t1=new Thread(()->{ secKill(); }); Thread t2=new Thread(()->{ secKill(); }); t1.start(); t2.start(); } }