Reids基础事务以及watch监控事务和回滚

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_38409944/article/details/84065143

Redis也是有事务的
Redis执行事务的三个过程:

1.开启事务 multi
2.命令进入队列
3.执行事务 exec

如果要求在一个连接中,在spring会使用SessionCallback接口来处理。这是为了减少性能损耗


redis的基础事务:

multi开启事务,直到exec执行事务,期间的命令将以队列形式存在,直到exec命令的出现,才会一次性发送队列里的命令去执行,执行过程中其他客户端也不能再插入任何命令了。也可以直接discard回滚事务(取消了事务中的命令),到exec时已经没有命令可以执行了。

注意开启事务后,命令不会马上执行,而是加入到队列中,只有当执行事务exec的时候才会顺序执行,其实还有一个watch命令,监听某些键,当exec的时候他会检测该键值有无发生变化,如果没有则执行命令,否则回滚事务。

举个例子:这里用同一个连接操作Redis命令还有Lambda表达式(代替匿名类)
注意不知道为什么,在idea中使用lambda表达式会出现红色的下划线提醒,但是可以执行,没有问题……疑惑ing

 @Test
    public void xx() {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        final RedisTemplate redisTemplate = applicationContext.getBean(RedisTemplate.class);
        SessionCallback sessionCallback = (SessionCallback)(RedisOperations ops) ->{
          ops.multi();//开启事务
          ops.boundValueOps("key1").set("value1");
          String value =(String) ops.boundValueOps("key1").get();
          System.out.println(value);
          List list = ops.exec();//list保存之前进入队列的所有命令的结果
            value =(String) redisTemplate.opsForValue().get("key1");
            return value;
        };
        String value =(String) redisTemplate.execute(sessionCallback);
        System.out.println(value);
    }

输出:

null
value1

第一个value输出null,原因是此时命令只是被加入到队列中,而咩有被执行,所有是null
第二个value已经执行完毕了
注意使用SessionCallback接口可以保证所有的命令都是通过一个Redis链接进行操作
如果想要得到Redis执行事务各个命令的结果可以使用List list = ops.exec();
**

再来探究下Redis的事务回滚:

事务回滚是为了保证数据的一致性性,要么同时完成,要么同时失败。
Redis的事务回滚和数据库有点不一样。
Redis事务遇到命令格式正确而数据类型不正确:

multi
set key1 value1
set key2 value2
incr key1
del key2
exec

注意:incr是自增,命令加入到队列的时候不会报错,但是执行exec的时候,就会遇到类型错误(value1是字符串而不是数字),而之前和之后的命令都会被执行,就本身不会执行。
如果是命令格式错误,那么直接事务回滚。

结论:

执行事务命令的时候,命令入队的时候Redis会检测事务的命令是否正确,如果不正确,全部命令回滚,相当于什么也没有执行。如果命令不正确,但是操作数据结构有问题,那么该命令执行错误,而其之前和之后的命令都会被正常执行。
所以对于一些重要操作,我们要检测数据的正确性以保证事务的正确执行,避免数据不一致。

那为什么redis要设置如此简陋的事务呢?
当然是为了保证最重要的问题-----性能。


再来看看:watch命令监控事务。

watch命令可以决定事务是执行还是回滚。watch命令,监控某些键值对,当执行事务exec的时候,首先会判断被watch命令监控的键值对前后是否发生变化,最后都会取消监控。所以watch是一次性消费的。
Redis在监控事务的时候参考了多线程的CAS(Compare And Swap),在数据高并发的操作中,这种机制也叫作乐观锁。但是有时候会存在ABA问题,即被监控的值变换为A-B-A,事务前后的值最终没有变,但是过程中改变了,如果此时并发处理就会出现问题。
但是在Redis事务中,并不阻塞其他连接的并发,而只是通过比较watch监控的键值对去保证数据的一致性,可以再非阻塞的多线程环境中并发执行,不会产生ABA问题,因为就算修改被watch监控的值为原来的值,还是会认为他被修改过了,有点绕。举个例子:
打开一个客户端:

./redis-cli               ./redis-cli 
set key1 value1                null
watch key1                     null
multi                          null
set key2 value2          set key1 value1
exec

返回结果(nil)
表示事务回滚了。

猜你喜欢

转载自blog.csdn.net/qq_38409944/article/details/84065143