在 MySQL 中,处理锁表问题

在 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_TRXINNODB_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>;

常见场景与注意事项

  1. DML 锁 vs DDL 锁

    • 上述方法主要解决 DML 锁 (如 UPDATE, DELETE, INSERT 导致的行锁或表锁)。
    • 如果是 DDL 锁 (如 ALTER TABLE 卡住),通常是因为有长事务正在使用该表。解决方法一样:找到那个长事务(INFORMATION_SCHEMA.INNODB_TRXtrx_started 很早的会话)并 KILL 它。
  2. 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

  3. 不要随意 KILL

    • KILL 命令会导致该会话的事务回滚。如果该事务已经修改了大量数据,回滚过程本身也可能耗时很长(期间锁依然不会立即释放,直到回滚完成)。
    • 在执行 KILL 前,最好先查看 INFO 字段,确认该 SQL 是否真的是异常卡死,还是正常的业务长跑。

总结操作流

  1. 执行 SELECT * FROM sys.innodb_lock_waits; (推荐) 或关联查询 performance_schema
  2. 记下 blocking_pid (阻塞者的 ID)。
  3. 检查阻塞者的 SQL (INFO 字段) 确认是否可以终止。
  4. 执行 KILL <blocking_pid>;
  5. 观察业务是否恢复。
相关推荐
BoomHe13 小时前
Android AOSP13 原生 Launcher3 壁纸获取方式
android
Digitally14 小时前
如何将联系人从 Android 转移到 Android
android
李小枫15 小时前
webflux接收application/x-www-form-urlencoded参数
android·java·开发语言
爱丽_15 小时前
MySQL `EXPLAIN`:看懂执行计划、判断索引是否生效与排错套路
android·数据库·mysql
NPE~15 小时前
[App逆向]环境搭建下篇 — — 逆向源码+hook实战
android·javascript·python·教程·逆向·hook·逆向分析
yewq-cn16 小时前
AOSP 下载
android
cch891817 小时前
Laravel vs ThinkPHP:PHP框架终极对决
android·php·laravel
米码收割机17 小时前
【Android】基于安卓app的汽车租赁管理系统(源码+部署方式+论文)[独一无二]
android·汽车
流星雨在线17 小时前
安卓使用 Startup 管理三方 SDK 初始化
android·startup
jwn99917 小时前
Laravel3.x:PHP框架的经典里程碑
android