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 可获得更完善的锁诊断能力
相关推荐
像风一样自由20202 分钟前
量化压缩实战:INT8 / INT4 / AWQ / GPTQ 全面对比
android·人工智能·语言模型·大模型
ULIi096kr19 分钟前
查看 MySQL 数据库容量大小:完整实用查询方法(含表数据、磁盘占用统计)
数据库·mysql
brycegao32126 分钟前
Android MVI进阶:纯原生实现Slot化可插拔架构
android·kotlin·架构设计·mvi·viewmodel
一条泥憨鱼31 分钟前
苍穹外卖【day3|菜品管理】
java·数据库·sql·mysql·mybatis
2601_961194021 小时前
27考研资料|百度网盘|夸克网盘
android·xml·考研·ios·iphone·xcode·webview
故渊at1 小时前
第二板块:Android 四大组件标准化学理 | 第十篇:ContentProvider 数据共享与 SQLite 引擎
android·jvm·数据库·sqlite·contentprovider
Kapaseker1 小时前
你遇到过 Kotlin 协程中的竞争问题吗?
android·kotlin
与水同流1 小时前
Android13 AIDL HAL服务实现Demo
android·hal·aidl
Database_Cool_1 小时前
数据仓库弹性扩缩容实践:阿里云 AnalyticDB MySQL 按需付费方案详解
数据仓库·mysql·阿里云
吴梓穆1 小时前
Python 基础语法2 if 运算符 循环
android·开发语言·python