记一次 MySQL出现“Lock wait timeout”错误的原因
Posted 蝉动
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了记一次 MySQL出现“Lock wait timeout”错误的原因相关的知识,希望对你有一定的参考价值。
先说原因: 手动开启事务,由于处理业务时间过长,既不提交也未报错回滚,长时间占用事务就会出现这种情况,错误
关键字:trx_state为 running
故障场景:在测试环境中,在修改订单中偶现Lock wait timeout,且一直重复出现
初步定位: 采用下列命令排查
select * from INFORMATION_SCHEMA.innodb_locks;
SELECT * FROM sys.innodb_lock_waits;
SELECT * FROM INFORMATION_SCHEMA.innodb_trx;
SELECT * FROM INFORMATION_SCHEMA.processlist;
innodb_locks中发现互斥锁,且等待语句仅仅为
update order set status = ‘1’ where id = ‘1593507966307274753’;
定位到锁住的事务 trx_id 340312222,发现事务running时间过长,且trx_query为null,这个是事务既没有提交也没有回滚的含义,那么就知道是事务340312222 一直占用order表,导致其他的DML操作无法继续进行下去,且超时等待
ps:没有选择直接延长lock wait time的时间,是因为没有数据库super权限
SELECT * FROM INFORMATION_SCHEMA.innodb_trx;
发现是订单表被锁了,且应该不是行锁,行锁只是锁住当前行,单条update主键语句,如果被锁,我的思路是应该是整表被锁了或者页锁,经过排查是表锁
进一步发现被等待的事务,是当天晚上19:40开始的,且一直未commit和rollback,长时间开启事务,除非连接断开或者被回收,才会关闭事务,排查事务原因,由于没有其他信息,故在系统业务日志中查看19:40左右的提交日志,定位到是手动开启的事务,
transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
//todo 更新了订单数据
//手动提交事务
dataSourceTransactionManager.commit(transactionStatus);
catch (Exception e)
dataSourceTransactionManager.rollback(transactionStatus);
e.printStackTrace();
return Result.error("推送材积信息失败,请联系管理员!");
且在逻辑中有查询和修改订单的逻辑,且有大批量处理数据,时间过长,导致既不提交也不会滚的情况,长时间的占用事务
参考文章:
MySQL出现“Lock wait timeout exceeded”错误的原因是什么?
mysql 事务一直running_事务一直running?记录一次事务异常导致的下单阻塞
记一次mysql的数据恢复
数据库版本:MySql5.7
操作系统:CentOS 7.7
引:我之前一直使用 Sql Server ,最近自己创业,为了省点钱,用了MySql
某天,我悠然的写着代码,调着BUG。我们的运营大佬突然跟我反馈了一个线上版本的问题:
说是数据突然不见了,我没怎么在意,心里在想,八成是客户自己把数据删了?? (PS:开发那么多年大部分灵异的反馈都是客户操作失误)
然而过了两分钟,运营大佬又反馈另一个客户也出现同样问题
此时我感觉不妙,但一时也想不起是什么问题,几分钟后,我突然.....想起了三天前把数据从 Sql Server 数据迁移到 MySql时写的那个测试脚本
我把它挂在了MVC Home/Index 路由下了。后来我发现navicat可以很方便的导入数据,就把它给忘了...忘了....忘了...,然后更新到了线上环境,今天突然不知道哪个没事干的兄弟直接访问了一下我的项目域名。默认跳转Home/Index ,结果悲剧了,数据恢复到了三天前...,万幸的是我这个脚本只写了一个表,也就是说,只有一张表出了点问题。
当务之急是赶紧恢复数据。Sql Server数据恢复我有经验,一句脚本就可以恢复到某个时间点,特方便。但MySql没恢复过数据【抓虾】
在恢复之前,我先做了一个操作,把需要恢复的表主键自增起始值拔高,避免恢复期间新增加的数据跟老数据主键冲突。
然后上网一搜,找到了一个类似于sqlserver的解决方案:binlog 日志恢复。以下是相关命令
1、链接数据库,查询日志开启状态(ON开启)(如果你的数据库没有开启这个,估计用不了下面的方法了,万幸我开启了)
mysql -u root -p
MySql>show variables like ‘log_%‘;
(看到有开启日志之后,我心已经放下半个,心里想这不就跟sqlserver一样,选个前几分钟的时间点,恢复那张表不就行了)
2、查询日志操作(看看已经记录了哪些日志)
MySql>show logs;
3、查看master状态(看现在哪个日志文件在用)
MySql>show master status;
4、刷新日志(让数据库日志从一个新的文件开始记录,老的我要用来做恢复)
flush logs;
5、导出整个库某个时间点之前的操作到sql(不要直接跟着操作哦,后面有坑)
mysqlbinlog /www/server/data/mysql-bin.000004(这个是日志文件的地址) --skip-gtids=true --stop-datetime=‘2020-07-14 17:35:30‘ --database=数据库名称 > /www/server/202007142245.sql
6、执行恢复
mysql -uroot -p密码 数据库名 < /www/server/202007142241upd.sql;
OK,我感觉已经大功告成,胜利在握了。
结果报错,说数据库已经存在...难道我的姿势不对?我得先删除数据库再操作???
好吧,备份当前数据库,删掉它。继续
mysql -uroot -p密码 数据库名 < /www/server/202007142241upd.sql;
继续报错,数据库不存在.....你想怎样....
于是我把脚本文件直接下载下来(600多M...),查看了一下内容,找到create database xxxxx 的时间,把第5步的导出脚本改成创建数据库之后
mysqlbinlog /www/server/data/mysql-bin.000004 --skip-gtids=true --start-datetime "2020-07-10 17:38:46" --stop-datetime=‘2020-07-14 17:35:30‘ --database=数据库名 > /www/server/202007142245.sql
再继续第6步
mysql -uroot -p密码 数据库名 < /www/server/202007142245upd.sql;
还是报错,表xxxx已存在.....,我后面又改了几次方案,又发现主键等其他报错...此时我已是提心吊胆,颤颤巍巍,如履薄冰,哆哆嗦嗦
终于在第n个小时时候我找到了解决办法,如下
创建一个新的数据库,把导出的脚本文件(202007142245upd.sql)中有关于老数据库的名称 全部替换成 新的数据库名称 (这里用了notepad++),然后继续执行第6步(注意:数据库名称也换成新的)
mysql -uroot -p密码 新数据库名 < /www/server/202007142245upd.sql;
等了十几分钟,终于全程没报错的执行完毕。
接下来就简单多了,因为我只有一张表数据被恢复到之前的状态,而且恢复数据期间表的主键有拔高,我直接将新库里面那张渠道表所有数据导入老库替换就大功告成。
但是如果你出问题的数据多,而且表数据会不断变化,那么你只能先停止你的应用。关闭所有可能导致数据库变化的链接。然后再进行恢复操作了。
以上
参考文章:
https://juejin.im/post/5d39839d6fb9a07ee74322ff#heading-1
https://blog.51cto.com/lvnian/1699627
以上是关于记一次 MySQL出现“Lock wait timeout”错误的原因的主要内容,如果未能解决你的问题,请参考以下文章