经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 数据库/运维 » Redis » 查看文章
【Redis】Redis事务:原子性与回滚的真相揭秘
来源:cnblogs  作者:马儿跑  时间:2023/9/25 16:49:53  对本文有异议

大家好,我是mep。今天一起来探讨一下Redis缓存的问题,SpringBoot如何集成Redis网上文章很多,基本都是介绍如何配置redisTemplate,如何调用,本文就不过多介绍了。这次我们研究的是:Redis的事务。

首先抛出一个问题,Redis支持事务吗?

 

 

答案肯定是支持,不然也不需要我们在这里探讨了。

然后你拿到关键词"Redis 事务"去搜索引擎搜索一下,得到了这样的答案:

Redis支持事务,But!Redis的事务不保证原子性,事务不会回滚。例如:我在Redis中提交了一个事务,包含3条命令,其中第2条命令报错了,并不会导致第一条命令的回滚,也不会阻止第三条命令的执行。

可是,真的是这样吗?你试过吗?哈哈,知道你懒得试,我来帮你们试试看喽!

先看一个我自己测试的例子,以下例子中RedisTemplate都开启了事务支持,否则测试没有意义,我的RedisConfiguration代码如下:

  1. @Configuration
  2. public class RedisConfiguration {
  3. @Bean
  4. public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
  5. RedisTemplate<String, Object> template = new RedisTemplate<>();
  6. template.setEnableTransactionSupport(true);
  7. template.setConnectionFactory(factory);
  8. template.setKeySerializer(new StringRedisSerializer());
  9. template.setHashKeySerializer(new StringRedisSerializer());
  10. template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
  11. template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
  12. template.afterPropertiesSet();
  13. return template;
  14. }
  15. }

 

例1,使用@Transactional注解,方法执行过程中报错,代码如下:

  1. @Transactional
  2. public void testRedisTransaction() {
  3. employeeMapper.updateByPrimaryKey(Employee.builder()
  4. .id(4L)
  5. .name("uuuuu")
  6. .gender(Gender.MALE)
  7. .build());
  8. redisTemplate.opsForValue().set("111", "111");
  9. int i = 1 / 0;
  10. redisTemplate.opsForValue().set("222", "111");
  11. }

执行前Redis缓存情况:

执行以上方法后,肯定会报错:

  1. java.lang.ArithmeticException: / by zero

猜猜执行完后数据库和Redis中数据操作是什么样的?

 

1.数据库会回滚,即update无效,这个并不意外,没啥可说的

2.执行后Redis缓存情况:

 空的?不是说Redis的事务不支持回滚吗?为什么key修改却无效了呢?

确实,因为Redis根本没有回滚,它的事务压根就没有提交!!!

这就是Redis的事务和关系型数据库不一样的地方,数据库一个事务中如果某一条SQL报错或方法中有RuntimeException(@Transactional默认)抛出的话,事务会回滚。对于Redis的事务来说,如果方法中抛RuntimeException的话,事务压根不提交,被DISCARD之后,自然不会执行。

如果你看到这里了,说明你一开始就质疑最上面搜索到的结果,连查到的知识都会质疑和验证,为什么要相信我上面说的事务压根就没有提交的结论呢?

带着疑问,我们继续验证,先上代码:

  1. @Transactional
  2. public void testRedisTransaction() {
  3. employeeMapper.updateByPrimaryKey(Employee.builder()
  4. .id(4L)
  5. .name("uuuuu")
  6. .gender(Gender.MALE)
  7. .build());
  8. System.out.println(1234);
  9. redisTemplate.opsForValue().set("111", "a");
  10. redisTemplate.opsForValue().set("222", "a");
  11. redisTemplate.exec();
  12. int i = 1 / 0;
  13. }

这次主动在报异常前提交了Redis事务,结果如下:

 到这里,我们得到结论是这样的:

Redis事务不能回滚,方法报异常时事务并没有回滚,之所以数据没有被写入到Redis,是因为事务被DISCARD了

根据我们查到的内容,还需要验证Redis的事务不能保证原子性,继续上示例: 

例2,使用@Transactional注解,在Redis事务中报错,代码如下:

  1. @Transactional
  2. public void testRedisTransactionOnly() {
  3. redisTemplate.opsForValue().set("333", "a");
  4. redisTemplate.opsForHash().put("333", "a", 111);
  5. }

正常来说,应该会报WRONGTYPE Operation的错误,不过,执行结果是这样的:

 甚至,连个错误都没有报!

是代码的问题吗?还是因为Redis的事务忽略了异常的命令,只执行了正常的命令?

继续测试,清空Redis,去掉@Transactional注解:

  1. // @Transactional
  2. public void testRedisTransactionOnly() {
  3. redisTemplate.opsForValue().set("333", "a");
  4. redisTemplate.opsForHash().put("333", "a", 111);
  5. }

执行结果:

 可见代码没有问题,确实会报错,只是提交到一个事务中,它不保证原子性,只执行了可执行的命令,即使后续的命令报错,也不会回滚,而且不会报错

至此,Redis事务相关的验证已结束。

结论就是我们开始搜索到的结果:

Redis支持事务,But!Redis的事务不保证原子性,事务不会回滚,提交后会执行可正常执行的命令,忽略报错的命令。

最后,来自Redis官网的一句话佐证我们的结论, 附出处:Transactions | Redis

原文链接:https://www.cnblogs.com/maerpao/p/17705258.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号