企业用户反馈进行某操作时遇到SQL错误“Lock wait timeout exceeded”,原因是事务获取资源时长时间被阻塞。可能由其他大事务占用相同行锁引起。解决措施包括找到并分析占锁事务原因,优化事务逻辑避免大事务生成;若实时发生,可调整锁等待时间或强制终止大事务,但后者需谨慎操作以避免数据问题。
1、问题背景:企业用户反馈进行某个事件操作时,显示以下报错信息,提示java.sql.SQLException: Lock wait timeout exceeded; try restarting transaction。
2、报错直接原因:当前事务在尝试获取资源时被阻塞了太长时间。在并发环境中,多个事务同时竞争相同的资源可能会导致这种情况发生。在当前场景下,结合报错的脚本,需要获取的是写入锁,已知表没被锁,被占用的是行锁,因此怀疑是否存在其他大事务在同一事件内占用该行锁。
3、解决措施:
3.1、找到占用锁的事务,分析事务久久不提交、不释放锁的原因,该措施是治本的方案。常见有以下原因:
(1) 当前事件操作会生成一个耗时较长的大事务,前端响应超时,用户以为操作失败,因此再次触发该事件操作。
排查方法:检查后台日志,根据报错的TraceId找到用户当前的操作,使用关键字去找临近时间是否存在相同的操作,若存在,通过判断是否操作的数据范围、事务耗时以判断是否由它占用所致的问题。事实上,本次问题就是该原因所致。
(2) 除开第一种原因,若开启了SQL日志的打印,此时就可以通过锁获取超时时打印脚本的信息,去排查相关日志。以背景错误为例,我们可以利用的关键词就有t_rec_receiveentry和fentryid的值,尝试找到行锁的事务。
(3) 无论是第一种还是第二种,本质上找到大事务的逻辑,返回到代码中进行逻辑分析。本次问题生成大事务的原因是,存在遍历中进行数据库IO的逻辑,后续的解决方案就是需要对此做性能优化,至此问题分析解决。
3.2、若问题是实时发生着的,可采用以下方案,临时解决问题:
(1) 增加锁获取等待时间:调整 innodb_lock_wait_timeout
参数的值,以避免这个问题的发生。但这应该是在彻底理解问题原因后的一种补救措施,而不是主要解决方法。
(2) 强制终止大事务。通过脚本
SELECT * FROM information schema.innodb trx
查询未提交事务,査到没有提交的只读事务(tx state="LOCKWAIT"),找到对应线程,执行kill命令,其中线程id为表中的trx_mysqlthread_id字段。
kill 线程ID
但该方法风险较高,很有可能会发生误删,务必确认这些进程是可以安全中断的。错误地结束进程可能会导致数据不一致或其他问题。
推荐阅读