错误
Lock wait timeout exceeded; try restarting transaction\n; Lock wait timeout exceeded; try restarting transaction; nested exception is com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Lock wait timeout exceeded; try restarting transaction
昨天线上一直报这个错误,看错误信息是lock锁 执行超时,后面锁一直等待,然后试着重启事务。
后续继续报这个错误很多次,还是同一批数据。
经过过查询此处代码发现整体的业务方法使用了事务
其中关键出代码更新update一些数据,数据量很大 ,update后又有索引,但是供应商提交选择数据很大,又继续提交。
更新数据lock锁,执行时间很长,超过了lock锁最大时间,然后报错,后面过程中又继续提交导致继续报错。
其实说了就是 2 个事务之间在等待锁,实例里update操作很长时间
出现这种情况,可以使用下面语句查询分析那个事务在等待锁(可能还需要排除死锁)
锁等待的对应关系
select * from information_schema.innodb_lock_waits;
当前出现的锁
select * from information_schema.innodb_locks;
当前运行的所有事务
select * from information_schema.innodb_trx;
当前线程详情
show full processlist;
如何复现
1、在同一事务内先后对同一条数据进行插入和更新操作
2、多台服务器或者多个线程操作同一数据库
3、瞬时出现高并发现象,spring事务造成数据库死锁(更新操作),后续操作超时抛出异常
4、事务 1对记录a进行更新/删除操作的请求未commit时,事务2也对记录a进行更新/删除操作。此时,2会等1提交事务,释放行锁。当等待时间超过innodb_lock_wait_timeout设置值时,会产生“LOCK WAIT”事务。
由于行锁是在不同的情况下获得的,让我们试着重现一个示例。
首先,我们将使用前面看到的登录MySQL脚本从两个不同的会话连接到服务器。
然后让在两个会话中运行下面的语句:
SET autocommit=0;
UPDATE TEST SET code = ‘11’ WHERE code = ‘22’;
10s后,第二个会话将失败:
mysql> UPDATE TEST SET code = '1 ’ WHERE code = ‘2’;
1205 - Lock wait timeout exceeded; try restarting transaction
Time: 11.227s
发生错误的原因是由于禁用了自动提交,第一个会话启动了一个事务。接下来,一旦UPDATE语句在事务中运行,就会获得该行的独占锁。但是,没有执行提交,使事务处于打开状态,并导致其他事务一直等待。由于提交没有发生,锁等待的超时达到了限制。这也适用于DELETE语句。
解决办法
-
事务中避免一次性处理太多数据(重点)
-
如需使用事务尽量细化
-
异步处理非事务代码
-
发现等待事务,如果过长根据业务情况可以kill
通过SELECT * FROM information_schema.innodb_trx查询未提交事务,查到一个一直没有提交的只读事务(trx_state=”LOCK WAIT”),找到对应线程,然后执行:kill 线程ID。线程id为表中的trx_mysql_thread_id字段。执行SELECT * from information_schema.
PROCESSLISTWHERE Time > 1000 AND USER = ‘xxx’ ORDER BY TIME desc; 找到线程 然后 kill 线程ID -
innodb_lock_wait_timeout 锁定等待时间改大
修改超时时间将 #innodb_lock_wait_timeout = 50 修改为 innodb_lock_wait_timeout = 500。
缺点:全局更改,影响也是全局的,等待时间加长,容易使等待事务增多导致堆积问题。