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 = Sleep且Time值很大(几百到几千秒)→ 疑似持锁连接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 = SleepTime值最大(长时间挂起)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 视图,无法直接查询谁持有元数据锁。只能通过以下方式间接判断:
SHOW FULL PROCESSLIST中 Sleep 时间最长的连接information_schema.innodb_trx中最早开始的事务- 升级到 MariaDB 10.x 可获得更完善的锁诊断能力