P0级事故,项目组慌的一批! 下

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: P0级事故,项目组慌的一批! 下

接下来我们验证下场景 4,先执行 Redis 事务操作,然后在不添加 @Transactional 注解的方法中执行 Redis 递增操作。

用 Postman 调用这个接口后,正常返回自增后的结果,并不是返回 null。说明在非 @Transactional 中执行 Redis 操作并没有受到 Redis 事务的影响。

四个场景的结论如下所示,只有第三个场景下,Redis 的递增操作才会返回 null。

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

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

三、源码解析

那我们就看下为什么开启了 Redis 事务支持,效果就不一样了。

找到 Redis 执行命令的核心方法, execute 方法。

然后一步一步点进去看,关键代码就是 211 行到 216 行,有一个逻辑判断,当开启了 Redis 事务支持后,就会去绑定一个连接(bindConnection),否则就去获取新的 Redis 连接(getConnection)。这里我们是开启了的,所以再到 bindConnection方法中查看如何绑定连接的。

image.png

接着往下看,关键代码如下所示,当开启了 Redis 事务支持,且添加了 @Transactional 注解时,就会执行 Redis 的 mutil 命令。

关键代码:conn.multi();

Redis Multi 命令 用于标记一个事务块的开始,事务块内的多条命令会按照先后顺序被放进一个队列当中,最后由 EXEC 命令原子性(atomic)地执行。

真相大白,开启 Redis 事务支持 + @Transactional 注解后,最后其实是标记了一个 Redis 事务块,后续的操作命令是在这个事务块中执行的。

比如下面的的递增命令并不会返回递增后的结果,而是返回 null。

stringRedisTemplate.opsForValue().increment("count", 1);

而我们的生产环境重启服务后,开启的 Redis 事务支持又被重置为默认值了,所以后续的 Redis 递增操作都能正常执行。

四、修复方案

目前想到了两种解决方案:

  • 方案一:每次 Redis 的事务操作完成后,关闭 Redis 事务支持,然后再执行 @Transactional 中的 Redis 命令。(有弊端
  • 方案二:创建两个 StringRedisTemplate,一个专门用来执行 Redis 事务,一个用来执行普通的 Redis 命令。

4.1 方案一

方案一的写法如下,先开启事务支持,事务执行之后,再关闭事务支持。

image.png

但是这种写法有个弊端 ,如果在执行 Redis 事务期间,在 @Transactional 注解的方法里面执行 Redis 命令,则还是会造成返回结果为 null。

4.2 方案二

弄两个 RedisTemplate Bean,一个是用来执行 Redis 事务的,一个是用来执行普通 Redis 命令的(不支持事务)。不同的地方引入不同的 Bean 就可以了。

先创建一个 RedisConfig 文件,自动装配两个 Bean。一个 Bean 名为 stringRedisTemplate 代表不支持事务的,执行命令后立即返回实际的执行结果。另外一个 Bean 名为 stringRedisTemplateTransaction,代表开启 Redis 事务支持的。

代码如下所示:

接下来在测试的 Service 类中注入两个不同的 StringRedisTemplate 实例,代码如下所示:

Redis 事务的操作改写成这样,且不需要手动开启 Redis 事务支持了。用到的 StringRedisTemplate 是支持事务的那个实例。

在 Spring 的 @Tranactional 中执行的 Redis 命令如下所示,用到的 StringRedisTemplate 是不支持事务的那个实例。

image.png

然后还是按照上面场景 3 的测试步骤,先执行 testRedisMutil 方法,再执行 testTransactionAnnotations 方法。

验证结果 :Redis 递增操作正常返回 count 的值,修复完成。

参考资料:https://blog.csdn.net/qq_34021712/article/details/79606551



相关文章
|
9月前
|
机器学习/深度学习 存储 人工智能
使用DeepSeek进行元学习:训练模型快速适应新任务
本文介绍了如何使用DeepSeek框架实现元学习(Meta-Learning),特别是模型无关的元学习(MAML)。通过详细的代码示例,展示了从环境准备、数据生成、模型构建到MAML算法的具体实现步骤。最终,训练出的模型能够在新任务上快速适应并表现出色。元学习在数据量有限或任务不断变化的场景中具有重要应用价值。
|
机器学习/深度学习 人工智能 测试技术
探索软件测试的未来:AI与自动化的融合之路
【9月更文挑战第9天】随着技术的飞速发展,软件测试领域正经历着一场革命。本文将深入探讨人工智能(AI)和自动化技术如何重塑软件测试的未来,提升测试效率,确保软件质量。我们将从AI在测试中的应用、自动化测试的优势,到两者结合带来的创新方法进行详细分析,旨在为读者提供一条清晰的路径,以理解并利用这些先进技术优化测试流程。
|
缓存 运维 监控
成为工程师 - 如何提升系统稳定性(1)
成为工程师 - 如何提升系统稳定性(1)
|
XML 存储 算法
【BBF系列协议】TR-106 CWMP端点和USP代理的数据模型模板
【BBF系列协议】TR-106 CWMP端点和USP代理的数据模型模板
374 3
|
JavaScript 算法 前端开发
彩某社js逆向
该内容是一个关于逆向工程的案例分析,讲述了如何破解财联社登录接口的加密参数`sign`。作者发现`sign`是通过`sha1`和`md5`混合加密的。经过调试,找到了加密算法的过程,并用JavaScript模拟了这个过程,最终成功生成了与接口相同的加密字符串。附有JS代码示例。
334 0
|
人工智能 机器人 测试技术
【Python】Python迭代器与生成器的区别(详细讲解)
【Python】Python迭代器与生成器的区别(详细讲解)
【Python】Python迭代器与生成器的区别(详细讲解)
|
SQL 安全 测试技术
2021年职业院校技能大赛“网络安全”项目 江西省比赛任务书—B模块
B模块涵盖安全事件响应和网络数据取证,涉及多项应用安全挑战。任务包括使用nmap扫描靶机、弱口令登录、生成反弹木马、权限验证、系统内核版本检查、漏洞源码利用、文件名和内容提取等。此外,还有Linux渗透测试,要求访问特定目录下的文件并提取内容。应用服务漏洞扫描涉及服务版本探测、敏感文件发现、私钥解密、权限提升等。SQL注入测试需利用Nmap扫描端口,进行SQL注入并获取敏感信息。应急响应任务包括处理木马、删除恶意用户、修复启动项和清除服务器上的木马。流量分析涉及Wireshark数据包分析,查找黑客IP、枚举测试、服务破解等。渗透测试任务涵盖系统服务扫描、数据库管理、漏洞利用模块搜索等。
392 0
|
图形学
Flutter笔记:Matrix4矩阵变换与案例
Flutter笔记:Matrix4矩阵变换与案例
762 0
|
对象存储
oss上传图片的图片名和url路径后缀不一致问题分析与说明
oss上传图片的图片名和url路径后缀不一致问题分析与说明
1041 0
|
Java 应用服务中间件 Apache
Spring Boot几种启动问题的解决方案
  使用Spring Boot以来,遇到和解决过好几次不同的项目启动问题,大多数事故起于错误的配置和依赖。因此,本文用于汇总这些问题,以及提供相应的解决方案,帮助大家更快的定位和排除故障。
3014 0