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 可获得更完善的锁诊断能力
相关推荐
修行者对66611 小时前
安卓阿里云镜像
android
刮风那天12 小时前
Android AMS创建进程不用Binder而用Socket?
android·java·binder
知行合一。。。14 小时前
Python--05--面向对象(继承,多态)
android·开发语言·python
それども14 小时前
怎么理解 LEFT JOIN 和 LEFT SEMI JOIN
java·数据库·mysql
Java成神之路-14 小时前
MySQL 索引跳跃扫描(Index Skip Scan)
mysql
jran-15 小时前
MySQL 用户与权限
数据库·mysql
無限進步D15 小时前
MySQL 排序与分页
数据库·mysql
张小潇15 小时前
AOSP15 WMS/AMS系统开发 -窗口动画源码分析
android
程序员陆业聪16 小时前
Shadow核心原理:壳子Activity与代理机制的精妙设计
android
唐青枫16 小时前
别只会写 IF:MySQL CASE WHEN 条件判断实战详解
sql·mysql