解决数据库死锁/取锁超时的一些经历

背景:

  刚从别人手里交接了一个服务, 里面有个复杂的业务(命名为接口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):
解决数据库死锁/取锁超时的一些经历_第1张图片

  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

你可能感兴趣的:(工作日常)