16-SpringBoot之Redis(三)——Redis事务机制

SpringBoot之Redis(三)——Redis事务机制

  • 1. Redis 事务执行过程
  • 2. 开启事务支持
  • 3. 测试
  • 4. 测试结果说明
  • 5. 源码下载

1. Redis 事务执行过程

Redis 事务执行过程如下图所示:
16-SpringBoot之Redis(三)——Redis事务机制_第1张图片

2. 开启事务支持

RedisConfig.java文件EnbaleTransactionSupport设为true

@Bean
    public RedisTemplate<String, Object> redisTemplate(JedisConnectionFactory jedisConnectionFactory ) {
        //设置序列化
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        // 配置redisTemplate
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
        redisTemplate.setConnectionFactory(jedisConnectionFactory);
        RedisSerializer stringSerializer = new StringRedisSerializer();
        redisTemplate.setKeySerializer(stringSerializer); // key序列化
        redisTemplate.setValueSerializer(stringSerializer); // value序列化
        redisTemplate.setHashKeySerializer(stringSerializer); // Hash key序列化
        redisTemplate.setHashValueSerializer(stringSerializer); // Hash value序列化
        redisTemplate.afterPropertiesSet();

        //开启事务支持
        redisTemplate.setEnableTransactionSupport(true);
        return redisTemplate;
    }

3. 测试

使用@Transactional:

@RequestMapping("/testTransactiont")
    @Transactional
    public Map<String, Object> testTransactiont() {

        redisTemplate.opsForValue().set("key1", "v1");

        //设置要监控的key1
        redisTemplate.watch("key1");
        //开始事务,在exec命令执行前,全部只是进入队列
        redisTemplate.multi();
        redisTemplate.opsForValue().set("key2", "v2");
        //redisTemplate.opsForValue().increment("key1",1); //①
        //v2值应为null
        Object v2 = redisTemplate.opsForValue().get("key2");
        System.out.println("命令在队列,所以v2为:" + v2);
        redisTemplate.opsForValue().set("key3", "v3");

        Object v3 = redisTemplate.opsForValue().get("key3");
        System.out.println("命令在队列,所以v3为:" + v3);
        //执行exec(),将先判别key1是否在监控后修改过,如果是则不执行事务,否则执行事务
        redisTemplate.exec();//②

        Map<String, Object> map = new HashMap<String, Object>();
        map.put("success", true);
        return map;
    }

4. 测试结果说明

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

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

5. 源码下载

源码下载地址:https://download.csdn.net/download/huangjun0210/10802867

你可能感兴趣的:(SpringBoot)