MySQL 中定位 DDL 被阻塞的问题及解决方案
来到golang学习网的大家,相信都是编程学习爱好者,希望在这里学习数据库相关编程知识。下面本篇文章就来带大家聊聊《MySQL 中定位 DDL 被阻塞的问题及解决方案》,介绍一下MySQL定位DDL、被阻塞,希望对大家的知识积累有所帮助,助力实战开发!
DDL 被阻塞了,如何找到阻塞它的 SQL?
经常碰到开发、测试童鞋会问,线下开发、测试环境,执行了一个DDL,发现很久都没有执行完,是不是被阻塞了?要怎么解决?
包括在群里,也经常会碰到类似问题:DDL 被阻塞了,如何找到阻塞它的 SQL ?
实际上,如何解决 DDL 被阻塞的问题,是 MySQL 中一个共性且高频的问题。
下面,就这个问题,给一个清晰明了、拿来即用的解决方案:
怎么判断一个DDL是不是被阻塞了 ?当DDL被阻塞时,怎么找出阻塞它的会话 ?
怎么判断一个 DDL是不是被阻塞了?
首先,看一个简单的Demo
session1> create table sbtest.t1(id int primary key,name varchar(10)); Query OK, 0 rows affected (0.02 sec) session1> insert into sbtest.t1 values(1,'a'); Query OK, 1 row affected (0.01 sec) session1> begin; Query OK, 0 rows affected (0.00 sec) session1> select * from sbtest.t1; +----+------+ | id | name | +----+------+ | 1 | a | +----+------+ 1 row in set (0.00 sec) session2> alter table sbtest.t1 add c1 datetime; 阻塞中。。。 session3> show processlist; +----+-----------------+-----------+------+---------+-------+---------------------------------+---------------------------------------+ | Id | User | Host | db | Command | Time | State | Info | +----+-----------------+-----------+------+---------+-------+---------------------------------+---------------------------------------+ | 5 | event_scheduler | localhost | NULL | Daemon | 47628 | Waiting on empty queue | NULL | | 24 | root | localhost | NULL | Sleep | 11 | | NULL | | 25 | root | localhost | NULL | Query | 5 | Waiting for table metadata lock | alter table sbtest.t1 add c1 datetime | | 26 | root | localhost | NULL | Query | 0 | init | show processlist | +----+-----------------+-----------+------+---------+-------+---------------------------------+---------------------------------------+ 4 rows in set (0.00 sec)
判断一个 DDL 是不是被阻塞了,很简单,就是执行 show processlist ,查看 DDL 操作对应的状态。
如果显示的是 Waiting for table metadata lock ,则意味着这个 DDL 被阻塞了。
DDL 一旦被阻塞了,后续针对该表的所有操作都会被阻塞,都会显示 Waiting for table metadata lock 。这也是 DDL 让人闻之色变的原因。
碰到了类似场景,要么 Kill DDL 操作,要么 Kill 阻塞 DDL 的会话。
Kill DDL 操作是一个治标不治本的方法,毕竟 DDL 操作总要执行。
除此之外,对于 DDL 操作,需要获取元数据库锁的阶段有两个:DDL 开始之初和 DDL 结束之前。如果是后者,就意味着之前的操作都要回滚,成本相对较高。
所以,碰到类似场景,我们一般都会 Kill 阻塞 DDL 的会话。
那么,怎么知道是哪些会话阻塞了 DDL 呢?
下面我们看看具体的定位方法。
定位方法
方法一:sys.schema_table_lock_waits
sys.schema_table_lock_waits 是MySQL 5.7引入的,用来定位 DDL 被阻塞的问题。
针对上面这个Demo。
我们看看sys.schema_table_lock_waits的输出。
mysql> select * from sys.schema_table_lock_waits\G
*************************** 1. row ***************************
object_schema: sbtest
object_name: t1
waiting_thread_id: 62
waiting_pid: 25
waiting_account: root@localhost
waiting_lock_type: EXCLUSIVE
waiting_lock_duration: TRANSACTION
waiting_query: alter table sbtest.t1 add c1 datetime
waiting_query_secs: 17
waiting_query_rows_affected: 0
waiting_query_rows_examined: 0
blocking_thread_id: 61
blocking_pid: 24
blocking_account: root@localhost
blocking_lock_type: SHARED_READ
blocking_lock_duration: TRANSACTION
sql_kill_blocking_query: KILL QUERY 24
sql_kill_blocking_connection: KILL 24
*************************** 2. row ***************************
object_schema: sbtest
object_name: t1
waiting_thread_id: 62
waiting_pid: 25
waiting_account: root@localhost
waiting_lock_type: EXCLUSIVE
waiting_lock_duration: TRANSACTION
waiting_query: alter table sbtest.t1 add c1 datetime
waiting_query_secs: 17
waiting_query_rows_affected: 0
waiting_query_rows_examined: 0
blocking_thread_id: 62
blocking_pid: 25
blocking_account: root@localhost
blocking_lock_type: SHARED_UPGRADABLE
blocking_lock_duration: TRANSACTION
sql_kill_blocking_query: KILL QUERY 25
sql_kill_blocking_connection: KILL 25
2 rows in set (0.00 sec)
只有一个 alter 操作,却产生了两条记录,而且两条记录的 Kill 对象还不一样,其中一条 Kill 的对象还是 alter 操作本身。
如果对表结构不熟悉或不仔细看记录内容的话,难免会 Kill 错对象。
不仅如此,在 DDL 操作被阻塞后,如果后续有 N 个查询被 DDL 操作堵塞,还会产生 N*2 条记录。
在定位问题时,这 N*2 条记录完全是个噪音。
这个时候,就需要我们对上述记录进行过滤了。
过滤的关键是 blocking_lock_type 不等于 SHARED_UPGRADABLE。
SHARED_UPGRADABLE 是一个可升级的共享元数据锁,加锁期间,允许并发查询和更新,常用在 DDL 操作的第一阶段。
所以,阻塞DDL的不会是SHARED_UPGRADABLE。
故而,针对上面这个 case,我们可以通过下面这个查询来精确地定位出需要 Kill 的会话。
SELECT sql_kill_blocking_connection FROM sys.schema_table_lock_waits WHERE blocking_lock_type 'SHARED_UPGRADABLE' AND waiting_query = 'alter table sbtest.t1 add c1 datetime';
方法二:Kill DDL 之前的会话
sys.schema_table_lock_waits 是 MySQL 5.7 才引入的。
但在实际生产环境,MySQL 5.6还是占有相当多的份额。
如何解决MySQL 5.6的这个痛点呢 ?
细究下来,导致 DDL 被阻塞的操作,无非两类:
表上有慢查询未结束。
表上有事务未提交。
其中,第一类比较好定位,通过 show processlist 就能发现。
而第二类仅凭 show processlist 很难定位,因为未提交事务的连接在 show processlist 中的状态同空闲连接一样,都是 Sleep 。
所以,网上有 Kill 空闲连接的说法,其实也不无道理,但这样做就太简单粗暴了,难免会误杀。
其实,既然是事务,在 information_schema.innodb_trx中肯定会有记录,如 session1 中的事务,在表中的记录如下,
mysql> select * from information_schema.innodb_trx\G
*************************** 1. row ***************************
trx_id: 421568246406360
trx_state: RUNNING
trx_started: 2022-01-02 08:53:50
trx_requested_lock_id: NULL
trx_wait_started: NULL
trx_weight: 0
trx_mysql_thread_id: 24
trx_query: NULL
trx_operation_state: NULL
trx_tables_in_use: 0
trx_tables_locked: 0
trx_lock_structs: 0
trx_lock_memory_bytes: 1128
trx_rows_locked: 0
trx_rows_modified: 0
trx_concurrency_tickets: 0
trx_isolation_level: REPEATABLE READ
trx_unique_checks: 1
trx_foreign_key_checks: 1
trx_last_foreign_key_error: NULL
trx_adaptive_hash_latched: 0
trx_adaptive_hash_timeout: 0
trx_is_read_only: 0
trx_autocommit_non_locking: 0
trx_schedule_weight: NULL
1 row in set (0.00 sec)
其中 trx_mysql_thread_id 是线程 id ,结合 information_schema.processlist ,可进一步缩小范围。
所以,我们可以通过下面这个 SQL ,定位出执行时间早于 DDL 的事务。
SELECT concat('kill ', i.trx_mysql_thread_id, ';')
FROM information_schema.innodb_trx i, (
SELECT MAX(time) AS max_time
FROM information_schema.processlist
WHERE state = 'Waiting for table metadata lock'
AND (info LIKE 'alter%'
OR info LIKE 'create%'
OR info LIKE 'drop%'
OR info LIKE 'truncate%'
OR info LIKE 'rename%'
)) p
WHERE timestampdiff(second, i.trx_started, now()) > p.max_time;
可喜的是,当前正在执行的查询也会显示在information_schema.innodb_trx中。
所以,上面这个 SQL 同样也适用于慢查询未结束的场景。
MySQL 5.7中使用sys.schema_table_lock_waits的注意事项
sys.schema_table_lock_waits 视图依赖了一张 MDL 相关的表-performance_schema.metadata_locks。
该表是 MySQL 5.7 引入的,会显示 MDL 的相关信息,包括作用对象、锁的类型及锁的状态等。
但在 MySQL 5.7 中,该表默认为空,因为与之相关的 instrument 默认没有开启。MySQL 8.0 才默认开启。
mysql> select * from performance_schema.setup_instruments where name='wait/lock/metadata/sql/mdl'; +----------------------------+---------+-------+ | NAME | ENABLED | TIMED | +----------------------------+---------+-------+ | wait/lock/metadata/sql/mdl | NO | NO | +----------------------------+---------+-------+ 1 row in set (0.00 sec)
所以,在 MySQL 5.7 中,如果我们要使用 sys.schema_table_lock_waits ,必须首先开启 MDL 相关的 instrument。
开启方式很简单,直接修改 performance_schema.setup_instruments 表即可。
具体SQL如下。
UPDATE performance_schema.setup_instruments SET ENABLED = 'YES', TIMED = 'YES' WHERE NAME = 'wait/lock/metadata/sql/mdl';
但这种方式是临时生效,实例重启后,又会恢复为默认值。
建议同步修改配置文件。
[mysqld] performance-schema-instrument='wait/lock/metadata/sql/mdl=ON'
总结
1. 执行 show processlist ,如果 DDL 的状态是 Waiting for table metadata lock ,则意味着这个 DDL 被阻塞了。
2. 定位导致 DDL 被阻塞的会话,常用的方法有两种:
2.1 sys.schema_table_lock_waits
SELECT sql_kill_blocking_connection FROM sys.schema_table_lock_waits WHERE blocking_lock_type 'SHARED_UPGRADABLE' AND (waiting_query LIKE 'alter%' OR waiting_query LIKE 'create%' OR waiting_query LIKE 'drop%' OR waiting_query LIKE 'truncate%' OR waiting_query LIKE 'rename%');
这种方法适用于 MySQL 5.7 和 8.0。
注意,MySQL 5.7 中,MDL 相关的 instrument 默认没有打开。
2.2 Kill DDL 之前的会话
SELECT concat('kill ', i.trx_mysql_thread_id, ';')
FROM information_schema.innodb_trx i, (
SELECT MAX(time) AS max_time
FROM information_schema.processlist
WHERE state = 'Waiting for table metadata lock'
AND (info LIKE 'alter%'
OR info LIKE 'create%'
OR info LIKE 'drop%'
OR info LIKE 'truncate%'
OR info LIKE 'rename%'
)) p
WHERE timestampdiff(second, i.trx_started, now()) > p.max_time;
如果 MySQL 5.7 中 MDL 相关的 instrument 没有打开或在 MySQL 5.6 中,可使用该方法。
今天关于《MySQL 中定位 DDL 被阻塞的问题及解决方案》的内容介绍就到此结束,如果有什么疑问或者建议,可以在golang学习网公众号下多多回复交流;文中若有不正之处,也希望回复留言以告知!
MySQL行转列详情
- 上一篇
- MySQL行转列详情
- 下一篇
- mysql语法之DQL操作详解
-
- 数据库 · MySQL | 18小时前 | MySQL · 数据库运维 · RESOURCE_GROUP Performance Schema MySQL RESOURCE GROUP CPU 资源 SET RESOURCE GROUP
- MySQL RESOURCE GROUP 怎样为查询分配 CPU 资源
- 369浏览 收藏
-
- 数据库 · MySQL | 18小时前 | MySQL · 主从复制 · 数据一致性 MySQL复制 replicate-wild-do-table 复制过滤 CHANGE REPLICATION FILTER
- MySQL 复制过滤规则的风险隔离方法
- 102浏览 收藏
-
- 数据库 · MySQL | 18小时前 | MySQL · InnoDB ·
- MySQL Instant DDL 变更列属性的边界
- 133浏览 收藏
-
- 数据库 · MySQL | 18小时前 |
- MySQL 重命名列时怎样避免旧应用读写失败
- 450浏览 收藏
-
- 数据库 · MySQL | 18小时前 | mysql 空间索引 SRID 地理范围 MBRIntersects
- MySQL 空间索引支持地理范围筛选的设计
- 114浏览 收藏
-
- 数据库 · MySQL | 18小时前 | MySQL · 主从复制 · 数据库运维 · 主从切换 MySQL GTID GTID自动定位 read_only super_read_only CHANGE REPLICATION SOURCE TO
- MySQL GTID 主从切换的检查清单
- 391浏览 收藏
-
- 数据库 · MySQL | 18小时前 |
- MySQL DDL 被元数据锁阻塞的定位方案
- 324浏览 收藏
-
- 数据库 · MySQL | 18小时前 |
- MySQL 直方图改善低基数条件估算的配置
- 163浏览 收藏
-
- 数据库 · MySQL | 18小时前 | 执行计划 · 查询优化 · mysql 执行计划 SHOW PROCESSLIST PROCESS权限 EXPLAIN FOR CONNECTION
- MySQL EXPLAIN FOR CONNECTION 为什么拿不到正在执行的计划
- 256浏览 收藏
-
- 数据库 · MySQL | 18小时前 |
- MySQL JSON 路径查询的生成列索引设计
- 296浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 505次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 563次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 590次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 541次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 353次使用
-
- MySQL JSON_TABLE 展开数组时如何保留缺失字段
- 2026-09-10 501浏览
-
- MySQL 分区表怎么处理跨分区唯一键:分区列约束与建表取舍
- 2026-08-30 501浏览
-
- MySQL权限管理设置全攻略
- 2026-03-29 501浏览
-
- MySQL分片实现方法及常见方案解析
- 2025-06-24 501浏览
-
- MySQL表空间碎片怎么清理?超详细优化教程
- 2025-06-13 501浏览
