在 MySQL 中,处理锁表问题通常分为三个步骤:查询锁等待情况 、定位阻塞源(持有锁的会话) 、释放锁(杀死会话)。
以下是针对不同 MySQL 版本的操作方案。
方案一:MySQL 5.7+ 及 MySQL 8.0+ (推荐)
新版本提供了 sys 库和增强的 performance_schema,查询非常直观。
1. 查询谁在等待锁,谁在阻塞别人
这是最关键的查询,它能直接告诉你 哪个会话(Blocking Session) 导致了 哪个会话(Waiting Session) 卡住。
SELECT
waiting.pid AS waiting_pid,
waiting.user AS waiting_user,
waiting.query AS waiting_query,
blocking.pid AS blocking_pid,
blocking.user AS blocking_user,
blocking.query AS blocking_query
FROM
performance_schema.data_lock_waits w
JOIN
performance_schema.data_locks l ON w.blocking_engine_lock_id = l.engine_lock_id
JOIN
information_schema.processlist waiting ON w.waiting_engine_thread_id = waiting.id
JOIN
information_schema.processlist blocking ON l.engine_thread_id = blocking.id;
或者使用更简洁的 sys 视图(如果已安装):
SELECT * FROM sys.innodb_lock_waits;
输出结果中的 blocking_pid 就是你需要杀死的进程 ID。
2. 查看具体的表锁情况
如果你想看具体哪张表被锁了:
SELECT
t.table_schema,
t.table_name,
t.index_name,
t.lock_type,
t.lock_mode,
t.lock_data,
p.id AS process_id,
p.user,
p.time,
p.state,
p.info
FROM
performance_schema.data_locks t
JOIN
information_schema.processlist p ON t.engine_thread_id = p.id
ORDER BY
t.table_name, p.time DESC;
3. 释放锁(杀死阻塞会话)
找到 blocking_pid 后,执行以下命令释放锁:
-- 语法:KILL <进程ID>;
KILL <blocking_pid>;
批量生成杀死语句(慎用):
如果你想一键杀死所有造成阻塞的会话(请先确认业务影响):
SELECT CONCAT('KILL ', blocking_pid, ';') AS kill_command
FROM sys.innodb_lock_waits;
复制执行结果中的 SQL 语句运行即可。
方案二:MySQL 5.6 及更早版本
老版本没有 data_lock_waits 表,需要通过关联 information_schema.INNODB_TRX 和 INNODB_LOCKS (5.6) 或仅靠 INNODB_TRX 来推断。
1. 查询正在运行的事务及锁等待
这个查询可以找出长时间未提交的事务(通常是锁表的元凶):
SELECT
t.trx_id,
t.trx_state,
t.trx_started,
t.trx_query,
p.id AS process_id,
p.user,
p.host,
TIMESTAMPDIFF(SECOND, t.trx_started, NOW()) AS duration_seconds
FROM
information_schema.INNODB_TRX t
JOIN
information_schema.PROCESSLIST p ON t.trx_mysql_thread_id = p.id
WHERE
t.trx_state != 'RUNNING' -- 或者是 'LOCK WAIT'
ORDER BY
t.trx_started ASC;
注意:在极老版本中,可能需要手动分析 SHOW ENGINE INNODB STATUS\G 的输出,查找 TRANSACTIONS 部分下的 HOLDS THE LOCK(S) 和 WAITING FOR THIS LOCK TO BE GRANTED。
2. 释放锁
同样使用 KILL 命令:
KILL <process_id>;
常见场景与注意事项
-
DML 锁 vs DDL 锁:
- 上述方法主要解决 DML 锁 (如
UPDATE,DELETE,INSERT导致的行锁或表锁)。 - 如果是 DDL 锁 (如
ALTER TABLE卡住),通常是因为有长事务正在使用该表。解决方法一样:找到那个长事务(INFORMATION_SCHEMA.INNODB_TRX中trx_started很早的会话)并KILL它。
- 上述方法主要解决 DML 锁 (如
-
Metadata Lock (MDL) 锁:
-
如果现象是:一个简单的
SELECT卡住,或者ALTER TABLE一直显示Waiting for table metadata lock。 -
查询 MDL 锁(MySQL 5.7+):
SELECT * FROM performance_schema.metadata_locks WHERE OWNER_THREAD_ID IS NOT NULL; -
这通常需要结合
performance_schema.events_statements_current找到持有 MDL 锁的线程 ID,然后KILL。
-
-
不要随意 KILL:
KILL命令会导致该会话的事务回滚。如果该事务已经修改了大量数据,回滚过程本身也可能耗时很长(期间锁依然不会立即释放,直到回滚完成)。- 在执行
KILL前,最好先查看INFO字段,确认该 SQL 是否真的是异常卡死,还是正常的业务长跑。
总结操作流
- 执行
SELECT * FROM sys.innodb_lock_waits;(推荐) 或关联查询performance_schema。 - 记下
blocking_pid(阻塞者的 ID)。 - 检查阻塞者的 SQL (
INFO字段) 确认是否可以终止。 - 执行
KILL <blocking_pid>;。 - 观察业务是否恢复。