redis——事务

一、Redis事务的基本用法

 1 127.0.0.1:6379> set int 0
 2 OK
 3 127.0.0.1:6379> multi        //begin  事务开始
 4 OK
 5 127.0.0.1:6379> incr int
 6 QUEUED                       //QUEUE  指令已经被服务器缓存到队列里了
 7 127.0.0.1:6379> incr int
 8 QUEUED
 9 127.0.0.1:6379> exec         //commit 事务提交 
10 1) (integer) 1
11 2) (integer) 2
12 127.0.0.1:6379> multi        //begin  事务开始
13 OK
14 127.0.0.1:6379> incr int
15 QUEUED
16 127.0.0.1:6379> incr int
17 QUEUED
18 127.0.0.1:6379> discard      //discard  事务丢弃
19 OK
20 127.0.0.1:6379> get int
21 "2"

 二、redis没有保证事务的的原子性

事务四大特性:原子性、隔离性、持久性、一致性

由于redis是单线程的,保证事务的隔离性,一致性,每个指令的原子性,redis持久性也能保持事务的持久性。但是redis没有保证多个指令的原子性,无法保证事务的原子性。

 1 127.0.0.1:6379> multi
 2 OK
 3 127.0.0.1:6379> set books java
 4 QUEUED
 5 127.0.0.1:6379> incr books
 6 QUEUED
 7 127.0.0.1:6379> set bookss golang
 8 QUEUED
 9 127.0.0.1:6379> exec
10 1) OK  //执行成功
11 2) (error) ERR value is not an integer or out of range //执行失败
12 3) OK  //执行成功
13 127.0.0.1:6379> get books
14 "java"  //没有回退rollback
15 127.0.0.1:6379> get bookss
16 "golang" //没有回退rollback

 三、优化

redis事务的每个指令到事务缓存队列时都要经过一次网络读写,所以通常Redis的客户端在执行事务时都会结合pipeline一起使用,可以将多次IO操作压缩为单次IO操作。

 1 public class TransactionTest {
 2 
 3     public static void main(String[] args){
 4         Pipeline pipeline = RedisUtils.pipelined();
 5         pipeline.multi();
 6         pipeline.incr("int");
 7         pipeline.incr("int");
 8         Response<List<Object>> response = pipeline.exec();
 9         pipeline.close();//get前需要关闭管道
10         System.out.println(response.get());
11         RedisUtils.close();
12     }
13 }

四、watch

watch在事务开始之前盯住一个或多个变量,当事务执行时,也就是服务器收到exec指令要顺序执行缓存的事务队列,Redis会检查关键变量自watch之后是否被修改。如果关键变量被修改过,exec指令就会返回NULL回复告知客户端事务执行失败。

 1 127.0.0.1:6379> watch int    //watch int
 2 OK
 3 127.0.0.1:6379> get int       
 4 "10"
 5 127.0.0.1:6379> incr int     //watch期间int被修改了
 6 (integer) 11
 7 127.0.0.1:6379> multi        //开启事务
 8 OK
 9 127.0.0.1:6379> incr int     
10 QUEUED
11 127.0.0.1:6379> exec         //执行事务
12 (nil)                        //执行失败 返回null

 注意:

①禁止在multi与exec之间执行watch指令,必须在multi之前执行,否则会报错

②multi后exec前,不能关闭redis连接,否则会抛错ERR EXE without MULTI

③watch与multi与exec必须是同一个redis连接实例,否则不会生效。

public class WatchTest {

    public static void main(String[] args){
        RedisUtils.CallWithRedis caller = new RedisUtils.CallWithRedis() {
            @Override
            public Object call(Jedis jedis) {
                for (;;){
//                    RedisUtils.watch("int");//watch multi exec必须是一个jedis实例
                    jedis.watch("int");
                    int value = Integer.parseInt(jedis.get("int"));
                    System.out.println("int : " + value);
//                    RedisUtils.incr("int");//这里修改操作可以是不同的连接
                    jedis.incr("int");
//                    Transaction transaction = RedisUtils.multi();//里面包含redis自动关闭连接,会导致exec报错
                    Transaction transaction = jedis.multi();
                    transaction.set("int","0");
                    List<Object> res = transaction.exec();
                    if(res != null){
                        System.out.println("transaction success");
                        break;
                    }
                    System.out.println("watchError!");
                }
                return 0;
            }
        };
        RedisUtils.watch(caller);
        RedisUtils.close();
    }
}

RedisUtils.java
public static Object watch(CallWithRedis caller){
return RedisUtils.execute(caller);
}
 
原文地址:https://www.cnblogs.com/wqff-biubiu/p/12299209.html