MySQL锁表诊断与解决方案

MySQL / MariaDB 锁表诊断与解决方案

数据库:jdbc:mysql://IP:3306/database

版本:MariaDB 5.5.68


一、问题现象

执行 SHOW FULL PROCESSLIST 后,发现多个连接处于 Waiting for table metadata lock 状态,说明存在表元数据锁争用,ALTER TABLE 操作被长时间阻塞。

特征 说明
阻塞类型 Table Metadata Lock(表元数据锁)
被阻塞操作 ALTER TABLE ds_lc_json_*(4个,等待超过 400~1200 秒)
根因 两个 Sleep 连接挂起约 2 小时,事务未提交/连接未关闭

二、诊断 SQL

1. 查看进程列表(最直接)

复制代码
SHOW FULL PROCESSLIST;

重点关注:

  • State 列出现 Waiting for table metadata lock → 有锁等待
  • Command = SleepTime 值很大(几百到几千秒)→ 疑似持锁连接
  • Info = NULL 的 Sleep 连接 → 事务未提交或连接泄漏

2. 查看当前所有活跃事务

复制代码
SELECT
  trx_id,
  trx_state,
  trx_started,
  TIMESTAMPDIFF(SECOND, trx_started, NOW()) AS duration_sec,
  trx_mysql_thread_id,
  trx_query,
  trx_tables_locked,
  trx_rows_locked
FROM information_schema.innodb_trx
ORDER BY trx_started ASC;

重点关注 duration_sec 较大(超过几十秒)且 trx_state 非正常的事务。


3. 查看锁等待关系

复制代码
SELECT
  r.trx_id AS waiting_trx_id,
  r.trx_mysql_thread_id AS waiting_thread,
  r.trx_query AS waiting_query,
  b.trx_id AS blocking_trx_id,
  b.trx_mysql_thread_id AS blocking_thread,
  b.trx_query AS blocking_query
FROM information_schema.innodb_lock_waits w
JOIN information_schema.innodb_trx b ON b.trx_id = w.blocking_trx_id
JOIN information_schema.innodb_trx r ON r.trx_id = w.requesting_trx_id;

4. 查看锁等待超时配置

复制代码
SHOW VARIABLES LIKE 'innodb_lock_wait_timeout';
SHOW VARIABLES LIKE 'wait_timeout';
SHOW VARIABLES LIKE 'interactive_timeout';

三、解决方案

步骤 1:定位持锁连接

SHOW FULL PROCESSLIST 结果中,找到:

  • Command = Sleep
  • Time 值最大(长时间挂起)
  • Info = NULL

这类连接极有可能是持有元数据锁的根源。

步骤 2:KILL 持锁连接

复制代码
-- 替换为实际的线程 ID
KILL <thread_id>;

-- 本次案例执行的命令
KILL 41039656;
KILL 41039657;

KILL 后,该连接的事务自动回滚,锁释放,等待中的 ALTER TABLE 会继续执行。

步骤 3:验证是否解决

复制代码
SHOW FULL PROCESSLIST;

确认 Waiting for table metadata lock 的行已消失,说明锁已释放。


批量生成 KILL 语句(等待超过 30 秒的事务)

复制代码
SELECT CONCAT('KILL ', trx_mysql_thread_id, ';') AS kill_sql
FROM information_schema.innodb_trx
WHERE TIMESTAMPDIFF(SECOND, trx_started, NOW()) > 30
  AND trx_state = 'LOCK WAIT';

四、预防措施

1. 设置连接空闲超时

复制代码
-- 空闲连接超过 300 秒自动断开
SET GLOBAL wait_timeout = 300;
SET GLOBAL interactive_timeout = 300;

也可写入 my.cnf 永久生效:

复制代码
[mysqld]
wait_timeout = 300
interactive_timeout = 300
innodb_lock_wait_timeout = 10

2. 设置锁等待超时

复制代码
-- 当前会话
SET SESSION innodb_lock_wait_timeout = 10;

-- 全局
SET GLOBAL innodb_lock_wait_timeout = 10;

3. 应用层排查连接泄漏

检查项 说明
连接池配置 确认设置了 testOnBorrow 或连接保活机制
事务管理 确保每个 BEGIN 都有对应的 COMMIT 或 ROLLBACK
事务粒度 避免在事务内执行 HTTP 请求、文件 IO 等耗时操作
DDL 时机 ALTER TABLE 尽量在业务低峰期执行,或使用 pt-online-schema-change

五、常见锁表原因汇总

原因 说明
未提交事务 代码里 BEGIN 后忘记 COMMIT / ROLLBACK
无索引的大批量操作 UPDATE / DELETE 没走索引,导致全表扫描锁行
DDL 操作 ALTER TABLE 申请表级元数据锁,会阻塞所有读写
事务内耗时操作 事务持续时间过长,长期占用锁
连接泄漏 连接池未正确回收连接,事务一直未关闭

六、MariaDB 5.5 特别说明

MariaDB 5.5 没有 performance_schema.metadata_locks 视图,无法直接查询谁持有元数据锁。只能通过以下方式间接判断:

  1. SHOW FULL PROCESSLIST 中 Sleep 时间最长的连接
  2. information_schema.innodb_trx 中最早开始的事务
  3. 升级到 MariaDB 10.x 可获得更完善的锁诊断能力
相关推荐
未来之窗软件服务1 小时前
CICD 信发系统自动打包安卓签名apk—无相无界(7)—东方仙盟
android·仙盟创梦ide·东方仙盟·东方仙盟无相无界
Mr -老鬼1 小时前
零基础玩转 EasyClick+ESP32 OTG有线HID|零权限超高稳定手机操控
android·智能手机
我命由我123451 小时前
Kotlin 开发 - 双冒号操作符(引用顶层函数、引用成员函数、引用构造函数、引用属性、引用类)
android·java·开发语言·kotlin·android studio·android jetpack·android-studio
2301_808414381 小时前
MySQL数据类型
数据库·mysql
我命由我123451 小时前
Kotlin 开发 - sealed 关键字
android·java-ee·kotlin·android studio·android jetpack·android-studio·android runtime
Digitally3 小时前
5 种简单方法:如何将华为手机照片传输到 Mac 电脑
android
それども12 小时前
DELETE 和 TRUNCATE TABLE区别
java·数据库·mysql
wenha12 小时前
数据库隔离级别
数据库·mysql·sqlserver·隔离级别
Edward1111111113 小时前
4.27mysql ,数据库,数据源
数据库·mysql