MySQLTransactionRollbackException: Lock wait timeout exceeded; try restarting transaction
上述这个错误,接触 MySQL 的或多或少应该都遇到过,这个报错我们称之为锁等待超时。在高并发场景下这个问题很容易出现。
这种错误就是平常说的两个业务相关事务的操作对相同的行锁冲突了,你不提交,我也不提交,导致这样的问题。为了方便理解我先使用mysql客户端来演示下
客户端1
start TRANSACTION;
update supplier set supplier_status = '2' where supplier_code = '1000001'
客户端2
start TRANSACTION;
update supplier set supplier_status = '3' where supplier_code = '1000001'
以上演示效果,是否看到了和应用系统程序一样报错的问题呢
那么如果线上出现这样的业务问题涉及锁等待超时时间过长,怎么来排查呢
这里我偷了懒,还是通过再次执行上面客户端执行的命令,根据那种情况,还原线上通过sql命令怎么来排查
为了方便演示,我这里设置下 事务超时时间久点
SET GLOBAL innodb_lock_wait_timeout=1500;
SHOW GLOBAL VARIABLES LIKE '%innodb_lock_wait_timeout%';
1.查看未提交的事务
select * from information_schema.INNODB_TRX;
2.查看正在锁的事务
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS;
3.查看正在等待锁的事务
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS;
4.查看sql线程
select * from information_schema.processlist where db like 'xxdb';
以上语句查询结果以及show full processlist 是显示用户正在运行的线程,需要注意的是,除了 root 用户能看到所有正在运行的线程外,其他用户都只能看到自己正在运行的线程,看不到其它用户正在运行的线程。除非单独个这个用户赋予了PROCESS 权限。
- Id: 就是这个线程的唯一标识,当我们发现这个线程有问题的时候,可以通过 kill 命令,加上这个Id值将这个线程杀掉,就是这个表的主键。
- User: 就是指启动这个线程的用户。
- Host: 记录了发送请求的客户端的 IP 和 端口号。通过这些信息在排查问题的时候,我们可以定位到是哪个客户端的哪个进程发送的请求。
- DB: 当前执行的命令是在哪一个数据库上。如果没有指定数据库,则该值为 NULL 。
- Command: 是指此刻该线程正在执行的命令。下面单独解释
- Time: 表示该线程处于当前状态的时间。
- State: 线程的状态,和 Command 对应,下面单独解释。
- Info: 一般记录的是线程执行的语句。默认只显示前100个字符,也就是你看到的语句可能是截断了的,要看全部信息,需要使用 show full processlist。
Command 各类值的解释:
- Binlog Dump: 主节点正在将二进制日志 ,同步到从节点
- Change User: 正在执行一个 change-user 的操作
- Close Stmt: 正在关闭一个Prepared Statement 对象
- Connect: 一个从节点连上了主节点
- Connect Out: 一个从节点正在连主节点
- Create DB: 正在执行一个create-database 的操作
- Daemon: 服务器内部线程,而不是来自客户端的链接
- Debug: 线程正在生成调试信息
- Delayed Insert: 该线程是一个延迟插入的处理程序
- Drop DB: 正在执行一个 drop-database 的操作
- Execute: 正在执行一个 Prepared Statement
- Fetch: 正在从Prepared Statement 中获取执行结果
- Field List: 正在获取表的列信息
- Init DB: 该线程正在选取一个默认的数据库
- Kill : 正在执行 kill 语句,杀死指定线程
- Long Data: 正在从Prepared Statement 中检索 long data
- Ping: 正在处理 server-ping 的请求
- Prepare: 该线程正在准备一个 Prepared Statement
- ProcessList: 该线程正在生成服务器线程相关信息
- Query: 该线程正在执行一个语句
- Quit: 该线程正在退出
- Refresh:该线程正在刷表,日志或缓存;或者在重置状态变量,或者在复制服务器信息
- Register Slave: 正在注册从节点
- Reset Stmt: 正在重置 prepared statement
- Set Option: 正在设置或重置客户端的 statement-execution 选项
- Shutdown: 正在关闭服务器
- Sleep: 正在等待客户端向它发送执行语句
- Statistics: 该线程正在生成 server-status 信息
- Table Dump: 正在发送表的内容到从服务器
- Time: Unused
那么我们看到事务表中INNODB_TRX,里面是否有正在锁定的事务线程,看看ID是否在show processlist里面线程中,如果有,那么就证明了这个线程事务一直没有commit(提交)或者roolback(回滚)而是卡住了,所以,我们需要人为介入,kill掉。
这个线程在干什么
select * from information_schema.processlist where id = 1738790
kill掉它
kill 1738790
实际操作的时候,我这里kill掉之后,有个客户端还是在等待,我又去查询了了下,未提交的事务
select * from information_schema.INNODB_TRX;
继续kill掉该线程id
这个时候等待事务提交的客户端出现
这里顺便记录下几个常用的监控语句
根据客户端 IP 分组,统计每个客户端的链接数
select client_ip,count(client_ip) as client_num from (select substring_index(host,':' ,1) as client_ip from processlist ) as connect_info group by client_ip order by client_num desc;
查看正在执行的线程,并按 Time 倒排序,看有没有执行时间特别长的线程
select * from information_schema.processlist where Command != 'Sleep' order by Time desc;
找出所有正在执行的线程并且执行时间超过 5 分钟,拼凑出 kill 语句
select concat('kill ', id, ';') from information_schema.processlist where Command != 'Sleep' and Time > 300 order by Time desc;
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步