排查锁等待步骤
最近线上碰到了几次mysql锁等待的问题,一个事务线程长期占用锁资源,导致其他事务无法获取到锁,为了快速解决问题,我们把线程kill掉了,但后面就定位不到具体的问题了,这里我总结整理一下我的方案。
如果事务刚好还处于等待状态
- 使用以下查询查看具体的锁等待情况,包括哪个事务持有了锁,哪个事务在等待哪个锁:
sql
SELECT * FROM sys.innodb_lock_waits;
这里可以看到当前 锁的类型,等待锁的事务,等待时间
同时我们可以看到等待的线程id,正在执行的sql,但这个sql不全,我们可以拿这个线程id去
information_schema.PROCESSLIST 查到完整sql
sql
select * from information_schema.PROCESSLIST WHERE ID = 12;
这里我们还可以定位到执行sql的host和端口
但这些不是我们主要关注的,等待的事务超过时间会回滚,我们需要找到阻塞线程做了什么操作,还是回到第一步,找到阻塞线程id,但我们无法定位到具体sql,因为这里只会把正在执行的sql查出来,所以如何找到阻塞线程执行过的sql成了关键。
- 找到阻塞线程执行过的sql
第一种:
前提是要打开 performance_schema,可以用下面sql看一下
sql
SHOW VARIABLES LIKE 'performance_schema';
但注意,如果是OFF,改成ON的话需要重启实例,一般线上环境是不会这么操作,而且打开后,因为要多记录一些日志信息,会影响整体性能,也不推荐打开。
用阿里云的RDS,修改参数时也会提示
查找阻塞线程的详细信息:
sql
SELECT * FROM performance_schema.threads WHERE processlist_id = 11;
查看引起阻塞的SQL语句:
sql
SELECT * FROM performance_schema.events_statements_history WHERE thread_id = 51;
这样我们就定位到了该线程执行过的sql,再通过information_schema.PROCESSLIST 找到host和port,去我们具体的服务定位问题。
第二种方案:
上面也提到performance_schema这个参数我们一般不会打开,所以我们只能通过事务id去binlog找了
1. 查询binlog文件
首先,我们需要找到包含我们需要查询的事务id的binlog文件。可以使用以下命令查看当前正在生成的binlog文件
sql
SHOW MASTER STATUS;
2.使用mysqlbinlog查找事务ID:
接下来,可以使用mysqlbinlog命令配合--start-position和--stop-position参数,逐步定位到含有特定事务ID的binlog记录。但是直接通过事务ID查找可能不直接支持,因为通常事务ID并不直接暴露为查询条件。一个更通用的方式是结合事务的时间戳或者其他上下文信息进行搜索。
如果你确切知道某个事务大致发生的时间,可以利用时间筛选,例如:
sql
mysqlbinlog --start-datetime="2024-07-31 14:00:00" mysql-bin.000001 | grep "事务相关的关键词或ID"
3.将binlog解析后的SQL语句输出到一个文本文件中
sql
mysqlbinlog mysql-bin.000001 > output.sql
mysqlbinlog --base64-output=DECODE-ROWS --verbose mysql-bin.000001 > output.sql
这样就可以从输出文件中找到我们事务的信息了
锁等待事务已经回滚
这种时候只能看看现在RUNNING的线程,看看是否像我们这种情况有事务长时间占用,通过事务id找到具体sql
sql
SELECT * FROM information_schema.INNODB_TRX;