栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

【MYSQL 问题/实践】Lock wait timeout exceeded Error

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

【MYSQL 问题/实践】Lock wait timeout exceeded Error

问题

最近遇到了一个线上问题,本质就是 mysql 在获取锁时超时了。

[40001][1205] Lock wait timeout exceeded; try restarting transaction
定位问题

首先肯定得看下这个报错是什么意思,又是怎么导致这个问题的。

先讲下背景知识(问题涉及的mysql 使用的存储引擎是 InnoDB):
在 mysql 事务中有时需要获取排他锁,既然是排他锁那就意味着同一时间只能被一个线程所占用,其他要获取同一锁的线程就只能等待。但是其他线程肯定不能无限期等待,要是前一个线程一直占着不释放怎么办?为了避免这种情况,InnoDB 就设置了一个系统变量 innodb_lock_wait_timeout。

innodb_lock_wait_timeout表示一个事务等待一个行锁的最长时间,超过指定时间就会报上述错误。默认是 50s。一般情况下产生上述问题的原因是当前事务在等待一个被其他事务占用的行锁,并且超过了innodb_lock_wait_timeout指定的时间。并且innodb_lock_wait_timeout只对 InnoDB 的行锁生效,其他锁比如表锁则不会生效。

复现问题

大概讲了一下产生问题的原因,为了理解更深刻,举个例子复现一下这个问题。

创建两张一样的表,分别插入一些数据。

CREATE TABLE table1 (
    id INT PRIMARY KEY AUTO_INCREMENT,
    data VARCHAr(50)
);
insert into table1(data) value ('data1');

create TABLE table2 like table1;
insert into table2(data) values ('data2'), ('data3');

按照以下顺序执行 sql。

执行顺序事务1事务2
1begin;begin;
2update table1 set data = ‘T1 is updating the row’ where id = 1;
3update table2 set data = ‘T2 is updating the row’ where id = 1;
4update table1 set data = ‘T2 is updating the row’ where id = 1;
5update table2 set data = ‘T2 is updating the row’ where id = 2;
6commit;
7commit;

在执行到第 4 条 update table1 set data = ‘T2 is updating the row’ where id = 1; 时并没有立即输出执行结果,而是执行了一会并返回了以下 error。

[40001][1205] Lock wait timeout exceeded; try restarting transaction

执行完事务后查看表中数据发现结果有点出乎意料。table1、table2中数据并没有都更新成 T2, 而是只有 table2更新成了 T2。这与我们之前对事务原子性认知不太一样,感觉事务像是执行了一部分,没有执行完。


这是怎么回事呢?InnoDB 规定,当发生获取行锁超时时,只会回滚报错的语句,而不是整个事务。并且如果报错的语句后面还有其他 sql,也会继续执行,直到 commit 或 rollback。

问题的影响

既然在事务中发生了获取行锁超时只会回滚报错的语句,那就无法保证一致性,所以大概率会对生产环境数据产生影响。遇到这种问题时,也要记得梳理对业务的影响。

获取行锁超时与死锁不同,死锁发生时整个事务会自动回滚,但获取行锁超时不会。

如何解决问题

既然获取行锁超时无法保证事务的原子性,那在报错时一起回滚是不是就可以了?InnoDB 中确实有一个环境变量 innodb_rollback_on_timeout用来保证原子性。 innodb_rollback_on_timeout用来设置当事务超时时是否回滚整个事务。默认是不回滚的,这种情况就会出现上面数据不一致的问题。当设置成 ON 时则会回滚整个事务。

虽然 InnoDB 提出了innodb_rollback_on_timeout变量保证超时时的事务一致性,但这个变量在生产环境并不建议设置成 ON 。原因是这个变量只能在 GLOBAL内有效,那就意味着连接同一 mysql 实例的所有客户端都会设置为 ON, 对业务的影响范围很大。更建议从产生这个问题的具体业务场景出发解决问题。

比如我遇到的问题具体业务场景为:占用行锁的请求执行耗时 252s,这是个极特殊的例子,远远超过正常的执行时间,且该问题只出现过一次。怀疑是当时数据库出现了一些性能问题导致,与业务代码本身关系不大。考虑到投入产出比,这个问题就没有处理,仅记录。
同理,大家也可以考虑实际的业务场景来决定具体的解决方案。

参考

How to Fix a Lock Wait Timeout Exceeded Error in MySQL

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1003002.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号