一次 Redis 事务使用不当引发的生产事故( 二 )


一次 Redis 事务使用不当引发的生产事故

文章插图
通过这个实验说明在 @Transactional 注解的方法里面执行 Redis 的操作并不会返回 null,结论我记录到了表格中 。
一次 Redis 事务使用不当引发的生产事故

文章插图
所以说上面的推论不成立(加了 @Transactional 注解并不影响),到这里线索似乎断了 。
2.4 推测三然后跟当时做这块功能的开发人员说明了情况,告诉他可能是 Redis 事务造成的,然后问有没有其他同学在凌晨执行过 Redis 事务相关的 Job 。
他说最近有同事加过 Redis 的事务功能,在凌晨执行 Job 的时候用到事务 。我将这位同事加的代码简化后如下所示:
一次 Redis 事务使用不当引发的生产事故

文章插图
下面是针对这段代码的解释,简单来说就是开启事务,将 Redis 命令顺序放到一个队列中,然后最后一起执行,且保证原子性 。
setEnableTransactionSupport表示是否开启事务支持,默认不开启 。
一次 Redis 事务使用不当引发的生产事故

文章插图
难道开启了 Redis 事务,还能影响 Spring 事务中的 Redis 操作?
2.5 验证推测三如下表,序号 3 和 序号 4 的场景都是开启了 Redis 的事务支持,两个场景的区别是是否加了 @Transactional 注解 。
一次 Redis 事务使用不当引发的生产事故

文章插图
为了验证上面的场景,我们来做个实验:
  • 先开启 Redis 事务支持,然后执行 Redis 的事务命令 multi和 exec。
  • 验证场景 3:在 @Transactional 注解的方法中执行 Redis 的递增操作 。
  • 验证场景 4:在非 @Transactional 注解的方法中执行 Redis 的递增操作
2.5.1 执行 Redis 事务首先就用 Redis 的 multi 和 exec 命令来设置两个 key 的值 。
一次 Redis 事务使用不当引发的生产事故

文章插图
如下图所示,设置成功了 。
一次 Redis 事务使用不当引发的生产事故

文章插图
2.5.2 @Transactional 中执行 Redis 命令接下来在标注有 @Transactional 注解的方法中执行 Redis 的递增操作 。
一次 Redis 事务使用不当引发的生产事故

文章插图
多次执行这个命令返回的结果都是 null,这不就正好重现了!
一次 Redis 事务使用不当引发的生产事故

文章插图
再来看 Redis 中 count 的值,发现每执行一次 API 请求调用,都会递增 1,所以虽然命令返回的是 null,但最后 Redis 中存放的还是递增后的结果 。
一次 Redis 事务使用不当引发的生产事故

文章插图

一次 Redis 事务使用不当引发的生产事故

文章插图
接下来我们验证下场景 4,先执行 Redis 事务操作,然后在不添加 @Transactional 注解的方法中执行 Redis 递增操作 。
一次 Redis 事务使用不当引发的生产事故

文章插图
用 Postman 调用这个接口后,正常返回自增后的结果,并不是返回 null 。说明在非 @Transactional 中执行 Redis 操作并没有受到 Redis 事务的影响 。
一次 Redis 事务使用不当引发的生产事故

文章插图
四个场景的结论如下所示,只有第三个场景下,Redis 的递增操作才会返回 null 。
一次 Redis 事务使用不当引发的生产事故

文章插图
问题原因找到了,说明 RedisTemplete 开启了 Redis 事务支持后,在 @Transactional 中执行的 Redis 命令也会被认为是在 Redis 事务中执行的,要执行的递增命令会被放到队列中,不会立即返回执行后的结果,返回的是一个 null,需要等待事务提交时,队列中的命令才会顺序执行,最后 Redis 数据库的键值才会递增 。

经验总结扩展阅读