背景:
刚从别人手里交接了一个服务, 里面有个复杂的业务(命名为接口A), 最近我们公司需要修复数据, 开了80台机器去调用接口A, 日志就各种 Lock wait timeout exceeded; try restarting transaction
备注:
1.接口A有事务
2.统计错误日志, 有很多个地方都会出现 Lock wait timeout exceeded; try restarting transaction(大概4个点)
解决问题的过程:
1.首先把事务去掉了, 用其他方式保证数据的完整性, 解决了一部分执行sql 取锁超时的问题
2.还剩下一些取锁超时的地方, 就去研究了下如何定位锁表的问题
3.通过自己的研究与实验, 定位到了是哪个地方锁表时间太长
研究/实验过程:
了解到 select * from information_schema.innodb_trx; 该语句能获取正在等待锁的sql
1.首先, 设置数据库为手动提交事务(在本地mysql数据库实验):
set autocommit = 0;
2.执行sql语句:
select * from base_product where id = 21 for update;
备注:这样就把id为21的数据锁住了
3.执行sql语句:
update base_product set remark = "test" where id = 21;
备注:
update sql 语句自白: 本update也要取id = 21的数据, 居然被锁住了,没法finish
4.执行sql语句:
select * from information_schema.innodb_trx;
执行结果如下图(可以看出update 语句状态是LOCK_WAIT):
5.过了1分钟报出:
[SQL] update base_product set remark = “test” where id = 21;
[Err] 1205 - Lock wait timeout exceeded; try restarting transaction
由于我这个服务, 出Deadlock found when trying to get lock的错误比较频繁, 也就是, 我可以不停的刷:
select * from information_schema.innodb_trx;
直到刷出我关注的sql状态是LOCK_WAIT, 然后立即去执行
select * from information_schema.processlist;
就能分析出是哪条sql把我需要的表给锁住了
结果:
我没有线上数据库的权限… 所以我去dba那里玩了一下, 刷了10分钟, 刷出来了, 找到了锁住表的sql…
附:
我去网上查如何只查对某个表操作的sql
网上的方式是(错误):
select * from information_schema.processlist where db = #{表名}
正确的做法应该是:
select * from information_schema.processlist where info like "%#{表名}%";
eg:
select * from information_schema.processlist where command = "Query" and info like "%base_product%";
关键字:
锁表, 死锁, 事务, 行级锁, Lock wait timeout exceeded