今天在测试环境上发现用户第三方登录出现问题,服务器日志报错如下:
出现这个问题感觉还是挺疑惑的,因为测试环境已经稳定运行了几个月的时间,一直没有出现过mysql事务锁的问题,于是开始着手查问题。
1. 问题分析
针对Lock wait timeout exceeded; try restarting transaction的错误出现的原因一般有:
在同一个事务内先后对库中同一条记录进行事务操作,如更新、删除等;
并发操作库中同一条记录,出现锁竞争,一个线程获取锁后迟迟不释放,导致另一个尝试获取锁的线程超时。
在innoDB引擎下,默认的innodblockwait_timeout参数设置锁等待的时间是50s,一旦数据库锁超过这个时间就会报错。
2. 查找问题
先从mysql角度来对问题进行分析,查看mysql server上的事务情况和锁的情况以及锁等待的情况。
2.1. 查看进程
SHOW PROCESSLIST;
输出结果为:
2.2. 查看事务情况
select * from informationschema.innodbtrx
其中字段说明如下(这里只简要列举关键的几个字段,需要了解更多的请自行到官网查询):
trx_state:事务状态
trx_start:事务开始时间
trxrequestedlockid:innodblocks.lock_id
trxwaitstarted:事务开始等待的时间
trxmysqlthread_id:事务线程id
trxtableslocked:事务拥有多少个锁
trxisolationlevel:事务隔离级别
在我们的环境中执行后,结果为:
对比可以看到,记录的状态都为RUNNING,也就是正在执行的事务,并没有锁。
如果是事务锁定的情况如何解决?看事务表INNODB_TRX,里面是否有正在锁定的事务线程,看看ID是否在show processlist里面的sleep线程中,如果是,就证明这个sleep的线程事务一直没有commit或者rollback而是卡住了,我们需要手动kill掉。
2.3. 查看锁情况
SELECT * FROM informationschema.INNODBLOCKs;
可以看到锁也是没有的。
2.4. 查看锁等待情况
SELECT * FROM informationschema.INNODBLOCK_waits;
当前环境中也并无锁等待情况,这时就开始往其他方面分析了。
2.5. 查看mysql日志情况
由于当前测试环境是一个mysql的主从环境,所以会有很多的binlog日志,所以猜测是不是因为binlog空间占用太多的原因。进入mysql存储目录后发现:
可以看到binlog日志文件应该是已经及时被定时清理了,但是mysqld.log和slow.log都比较大,查看mysqld.log,会发现有大量的:
经过定位,发现这个是一个同事最近在调整的统计信息的定时任务,不小心把定时任务的频率调大了,导致频繁刷mysqld.log,定时任务频率恢复正常后,log日志不再增长。slow.log也是因为统计信息中的一些sql的问题导致的。
清空这两个文件:
echo > mysqld.log
echo > slow.log
复制
清空成功后,再尝试登录时,成功了。
虽然这里不是binlog的问题,但是还是有必要在这里安利下binlog的管理方法:
登录mysql:mysql -u root -p
show binary logs; show variables like '%log%';show master logs;查看多少binlog日志,占用多少空间。
手动清理1:PURGE MASTER LOGS TO 'mysql-bin.002467'; 删除mysql-bin.002467以前所有binlog,这样删除可以保证*.index信息与binlog文件同步。
手动清理2:PURGE MASTER LOGS BEFORE DATESUB(CURRENTDATE, INTERVAL 10 DAY); 手动删除10天前的binlog日志。
设置自动清理:set global expirelogsdays = 5; 把binlog的过期时间设置为5天; mysql> flush logs; 刷新log使上面的设置生效。为保证在MYSQL重启后仍然有效,在my.cnf中也加入此参数设置expirelogsdays = 5。
更多可以参考:https://blog.csdn.net/atco/article/details/24259333
3. 总结
出现Lock wait timeout exceeded; try restarting transaction的原因一般为:
多线程并发更新时,一个线程事务操作比较耗时,导致其他线程获取锁超时,这个需要查看代码问题并且要kill掉mysql中锁死的线程。
binlog日志文件过大,空间不足,清理binlog文件。
其他mysql日志文件过大,清理日志文件。
其他原因。