在 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. 观察业务是否恢复。
相关推荐
星轨初途2 小时前
郑州轻工业大学“筑梯杯” 2025级新生程序设计大赛暨省内高校邀请赛——题解
android·c++·经验分享·笔记·算法
黄林晴2 小时前
Android内核引入AuroFDO,你的App变快了
android
IT痴者2 小时前
Kotlin 开发注意事项(Android Java 开发者转型指南)
android·java·kotlin
Kapaseker2 小时前
你可能还不知道 Compose Pager 有多强大
android·kotlin
阿捏利2 小时前
vscode+jadx-mcp-server配置及使用
android·apk·逆向·mcp·jadx
程知农2 小时前
Android的配置笔记
android·笔记
幸福在路上wellbeing2 小时前
Android 程序员 常用的AI工具有哪些
android·人工智能
阿拉斯攀登2 小时前
【RK3576 安卓 JNI/NDK 系列 03】JNI 核心语法(上):数据类型映射与方法调用
android·安卓ndk入门·jni方法签名·java调用c++·rk3576底层开发
XerCis2 小时前
安卓手机搭建Samba服务器SMB
android·服务器·智能手机