现象
这次版本发完生产不久,主管就在群里通知了数据库有性能问题,产生了死锁。
这条SQL一直没有执行结束,导致系统其它服务都出现性能问题。
背景
这是一条更新人员索引日志表的SQL,这张表的作用就是用来标记哪些人员的数据发生变化。
涉及这条SQL的模块有后台任务和操作人员数据的功能。
后台任务从这张表获取需要更新的人员,将数据同步到solr。
操作人员数据的功能需要在这张表中标记该人员为待更新。
分析
产生问题的可能分为两个模块:一个是后台任务,一个操作人员数据的功能。
这两个模块如果同时更新同一个人员,并且一方的执行时间比较长,事务未提交,而另一方在此时提交事务,那就会产生问题。
后台任务
逻辑不复杂,而且包含更新日志表的操作的事务逻辑简单,不存在耗时问题
操作人员数据的功能
拿审核人员举例,审核人员包括一系列的流程:校验参数、更新信息、更新日志表、调用接口、发送邮件。
这一系列流程都是在同个事务中进行的,调用接口和发送邮件是比较耗时的操作。原先是没啥问题的,但是后来加的更新日志表,这个方法操作的表就是跟后台任务更新人员索引操作的表是同一张表。
产生问题的过程
1、该人员在日志表中的状态为未更新(其他操作导致状态为未更新)
2、正在执行审核该人员的功能,由于耗时较长,事务还未提交
3、后台任务遍历到了该人员,准备更新状态时,就出现了问题
解决方法
对Spring事务传播行为有所了解的朋友,应该就知道解决方法了。(Spring默认的事务传播行为是REQUIRED
)
既然由于审核操作耗时太久,更新日志表的事务未能及时提交,那就想方法把更新日志表的事务先提交,别占用太多时间。
我这里使用的方法是在更新日志表的方法上加上@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
,这里的意思就是为该方法开启一个新的事务,当该方法结束时就提交事务。这样一来这个简单的方法就可以马上提交对日志表的操作,而不会因为审核操作太耗时而占用太多时间。