【mysql@Transanction注解引发的生产问题】

一、业务背景

手机端调用本服务A,需要实时获取用户openId,该openId由第三方服务B返回。

二、业务处理

由于服务B返回值的时间不确定,故通过发布订阅的方式,设定一个主题,接收第三方服务返回信息,一旦获取到,则将openId入库。与此同时,手机端还在实时等待openId返回,所以在服务A中设定了一个TimeTask,每个一秒去查询库中是否获取到openId,超过3秒还没结果,则直接报错给手机端。

三、现象

第三方服务早早地已经把结果返回回来了,但是始终没有入库,直到服务A的定时任务超时后,报错给手机端,此时立即完成了openId的入库。也就是入库这个动作似乎一直在等服务A执行完才开始运行,可见大概率是被什么锁定住了。

四、分析

第三方服务和主题订阅中的业务,都涉及同一张表同一条数据的操作,所以大概率是记录被锁定了。

本地尝试重现该问题:用postman充当手机端发送请求,为了有更充裕的时间来分析该现象,所以将定时任务的执行次数设定为1000次,同时新增一个接口模拟订阅方对openId的入库。

准备工作完成,那么先调用定时任务接口,一直等待openId的入库。再调用订阅方的接口,模拟接收到了openId。问题重现!通过查看mysql锁的情况查出结果如下:

可以看出对主键7170这条记录存在锁定的情况。主题接收方一直想去入库,无奈有人抢先拿到了这条记录的锁,可见程序A中有加锁操作。程序A中也要对此条记录进行update,什么时候加锁的呢?

就是这玩意,加上该注解,涉及修改的操作会加上锁,直到程序A的接口执行完,锁才释放。

五、解决方案

去掉注解Transanction即可。服务A接口只涉及到一张表的操作,无须此注解。

如果涉及多表修改要加上注解,因为本业务是对同一张表同一条记录进行update,才会出现此问题。可以新增一张表,一张表是主业务表,一张是类似中间临时表,可以避免同表同记录的操作,最后把中间表的数据读入主表即可(只是举个例子,其实可以存redis或静态容器里)。

也可以不加事务注解,自己写回滚补偿操作(不是很推荐)。

六、补充

最后放上一些可能用得上的mysql锁信息查询语句:

show PROCESSLIST; --查询正在处理中的进程
KILL id(例如:57532165) ;可以将上述查询到的可能是死锁的id杀掉

show OPEN TABLES where In_use > 0; --查询锁定的表

SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS; --查询innodb引擎下的锁记录

SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS; ----查询innodb引擎下等待中的锁记录

SELECT VERSION(); --查看数据库服务端版本

SELECT @@global.tx_isolation; --查询事务隔离级别

SELECT * FROM information_schema.engines; --查看引擎类型

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值