当前位置:   article > 正文

redis事务详解-避免踏坑_redis @nullable

redis @nullable

redis事务简介

redis是支持一定事务能力的NoSQL,在redis中使用事务,通常的命令组合是watch…multi…exe,也就是要在一个redis连接中执行多个命令,这是我们可以考虑使用SessionCallback接口来达到这个目的。

redis事务流程

在这里插入图片描述
如上redis事务执行流程,首先watch监控redis的一些键;multi命令开启事务,开启事务后,接下来redis命令不会马上被执行,而是存放在一个队列里,这点是需要注意的地方,也就是在这是我们执行一些返回数据的命令,redis也不会马上执行,所以此时调用redis命令,返回的结果都是null;exec命令意义在于执行事务,只是它在队列命令执行前会判断watch监控的redis的键的数据是否发生过变化(即使赋予与之前相同的值也会被认为是变化过),如果发生变化,则redis取消事务,队列中命令全部不执行,否则执行事务,队列中命令全部执行,这里注意如果执行队列中的某一个命令错误,而错误后面的命令依旧被执行,这就是redis事务和数据库事务的不一样,redis事务是先让命令进入队列,一开始并没有检测这个命令是否能成狗,只有在exec命令执行时才发现错误。为了克服这个问题,一般在执行redis事务前,严格的检查数据,避免这样的情况发生。

实例代码

public Object multi(){
        redisTemplate.opsForValue().set("key1","value1");
        //SessionCallback实现在同一连接下执行多个redis命令
        redisTemplate.execute(new SessionCallback() {
            @Nullable
            @Override
            public Object execute(RedisOperations operations) throws DataAccessException {
                //设置要监控的key
                operations.watch("key1");
                //开启事务,在exec命令执行前,全部都只是进入队列
                operations.multi();
                operations.opsForValue().set("key2","value2");
                //operations.opsForValue () .increment ("key1", 1); //--1--
                //取值将为null,因为redis只是把命令放入队列,并没执行
                Object value2=operations.opsForValue().get("key2");
                System.out.println("命令在队列,所以value为null【"+value2+"】");

                operations.opsForValue().set("key3","value3");
                //取值将为null,因为redis只是把命令放入队列,并没执行
                Object value3=operations.opsForValue().get("key3");
                System.out.println("命令在队列,所以value为null【"+value3+"】");

                //执行exec命令,将先判断key是否在监控后被修改过,如果是则不执行事务,否则就执行事务
                return operations.exec(); //--2--
            }
        });
        return "ok";
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

为了揭示 Redis 事务的特性,我们对这段代码做以下两种测试。

  • 先在 Redis 客户端清空 key2 和 key3 两个键的数据,然后在②处设置断点,在调试的环境下让请求达到断点,此时在 Redis上修改 keyl 的值,然后再跳过断点,在请求完成后在Redis上查询 key2 和 key3 值,可以发现 key2、key3 返回的值都为空(null),因为程序中先使得 Redis的watch命令监控了 key1 的值,而后的 multi 让之后的命令进入队列,而在 exec 方法运行前 我们修改了key1,根据 Redis 事务的规则,它在 exec 方法后会探测 key1是否被修改过,如 果没有则会执行事务,否则就取消事务, 所以 key2 和 key3 没有被保存到 Redis 服务器中。
  • 继续把 key2 和 key3 两个值清空,把①处的注释取消,让代码可以运行,因为 keyl 是一个 字符串,所以这里的代码是对字符串加一,这显然是不能运算的。同样地,我们运行这段代 码后,可以看到服务器抛出了异常,然后我们去 Redis 服务器查询 key2 和 key3,可以看到 它们已经有了值。注意,这就是 Redis 事务和数据库事务的不一样,对于 Redis 事务是先让 命令进入队列,所以一开始它并没有检测这个加一命令是否能够成功,只有在 exec 命令执 行的时候,才能发现错误,对于出错的命令 Redis 只是报出错误,而错误后面的命令依旧被 执行,所以 key2 和 key3 都存在数据,这就是 Redis 事务的特点,也是使用 Redis 事务需要 特别注意的地方。为了克服这个问题, 一般我们要在执行 Redis 事务前,严格地检查数据, 以避免这样的情况发生。

补充说明

redis在2.6版本后提供了Lua脚本的支持,而且在执行Lua脚本在redis中还具备原子性,所以在需要保证数据一致性的高并发环境中,我们也可以使用redis的Lua语言来保证数据的一致性,而且Lua脚本具备更加强大的运算功能,在高并发需要保证数据一致性时,Lua脚本方案比使用redis自身提供的事务要更好一些。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/318497
推荐阅读
相关标签
  

闽ICP备14008679号