版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/hfismyangel/article/details/81908880
当时的业务场景是要为从腾讯云队列拉取的数据做路由,分发到4个环境里(dev/test/preonline/online),先根据环境从不同的db里查询一条记录,如果记录满足条件则通过rest接口调用不同环境的接口修改该条记录。
排查过程:
select * from information_schema.INNODB_LOCK_WAITS
select * from information_schema.INNODB_TRX
show engine innodb status
show variables like 'autocommit'
show PROCESSLIST
查询LOCK_WAITS表发现有一条未提交的事务,查询为空,下面还有一条处于LOCK_WAIT状态的事务,可以看到是UPDATE。
之所以出现锁等待超时的原因是mysql不会为select语句加锁,但执行这段sql的代码被aop的全局事务覆盖了,并设置为了只读事务,这时mysql会为select加上偏向共享锁(IS锁,也称读锁),即允许多个事务同时加S锁/IS锁而不能加X锁,而update、delete、insert语句mysql会为其加上排他锁(X锁,sql后加上for update),加上X锁的事务无法加上任何其它锁,除非释放当前X锁,由于执行select的代码里没有执行完(在等待update的执行结果)不会自动提交,导致共享锁不会释放,而update的排他锁又加不上,在等待select的事务释放共享锁,从而产生死锁。解决方法很简单,将执行select的代码从事务中摘出来,不加事务直接执行即可,当然也是有条件的,insert这条记录的时间和select该记录的时间间隔在5s以上,基本可以确保不会产生脏读问题。
排查的时候特意看了一眼autocommit,是因为之前踩过这个坑,mysql的autocommit被关闭了,导致那一段时间脏读锁行锁表的情况发生很频繁,但万万没想到是autocommit被关了,导致事务执行着就被挂起来。
MySQL InnoDB 锁兼容阵列
X | IX | S | IS | |
X | ✗ | ✗ | ✗ | ✗ |
IX | ✗ | ✓ | ✗ | ✓ |
S | ✗ | ✗ | ✓ | ✓ |
IS | ✗ | ✓ | ✓ | ✓ |