MySQL 中如何定位 DDL 被阻塞的问题
经常碰到开发、测试童鞋会问,线下开发、测试环境,执行了一个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 被阻塞的问题的更多相关文章
- MySQL 5.6中如何定位DDL被阻塞的问题
在上一篇文章<MySQL 5.7中如何定位DDL被阻塞的问题>中,对于DDL被阻塞问题的定位,我们主要是基于MySQL 5.7新引入的performance_schema.metadata ...
- MySQL 5.7中如何定位DDL被阻塞的问题
在上篇文章<MySQL表结构变更,不可不知的Metadata Lock>中,我们介绍了MDL引入的背景,及基本概念,从“道”的层面知道了什么是MDL.下面就从“术”的层面看看如何定位MDL ...
- 【科普】MySQL中DDL操作背后的并发原理
一. 简介 DQL:指数据库中的查询(select)操作. DML:指数据库中的插入(insert).更新(update).删除(delete)等行数据变更操作. DDL:指数据库中加列(add co ...
- MySQL中的DDL,DML
MySQL中的DDL,DMLDDL:数据定义语言: CREATE,ALTER,DROP DB组件:数据库.表.索引.视图.用户.存储过程.存储函数.触发器.事件调度器等 CR ...
- mysql 5.6 在线 DDL
原文链接地址:http://seanlook.com/2016/05/24/mysql-online-ddl-concept/ 做MySQL的都知道,数据库操作里面,DDL操作(比如CREATE,DR ...
- mysql 5.6 online ddl
innodb存储引擎实现online ddl的原理是在执行创建或删除操作的同时,将DML操作日志写入到一个缓存中,待完成索引创建后再重做应用到表上,以此达到数据的一致性,这个缓存大小由参数innodb ...
- 物联网应用中实时定位与轨迹回放的解决方案 – Redis的典型运用(转载)
物联网应用中实时定位与轨迹回放的解决方案 – Redis的典型运用(转载) 2015年11月14日| by: nbboy| Category: 系统设计, 缓存设计, 高性能系统 摘要 ...
- MySQL 中隔离级别 RC 与 RR 的区别
1. 数据库事务ACID特性 数据库事务的4个特性: 原子性(Atomic): 事务中的多个操作,不可分割,要么都成功,要么都失败: All or Nothing. 一致性(Consistency): ...
- 存储引擎和表的操作(mysql中的数据类型、完整性约束)
一.存储引擎 .概念 MySQL中的数据用各种不同的技术存储在文件(或者内存)中.这些技术中的每一种技术都使用不同的存储机制.索引技巧.锁定水平并且最终提供广泛的不同的功能和能力. 通过选择不同的技术 ...
随机推荐
- Tableau预测指示器的运用
一.将订单日期拖拽两次到列,日期格式设置为年订单日期和月订单日期 二.将销售额拖拽至行,对应结果如下图所示 三.分析-趋势线-显示趋势线-显示选择整个视图 四.右键预测的任意位置,选择预测-描述预测- ...
- 关于某 App 请求参数 sign 字段加密分析
受害者: 6ZqG5LyX5pWw5o2u 通过 Charles 抓包发现关键信息请求均携带 sign 参数,且每次请求的值都不一样: 使用 jadx 将对应的 apk 反编译并分析,全局搜素 &qu ...
- Hyper-v安装Centos7
开篇语 知识库地址:https://azrng.gitee.io/kbms 介绍 可以让你在你的电脑上以虚拟机的形式运行多个操作系统(至于为什么选择这个,主要是系统已经自带了,所以能不装其他我就先不装 ...
- java 多线程 Thread.join子线程结束父线程再运行;join(long):等待超时毫秒数
Join的使用 目的:当子线程运行结束后,父线程才能再继续运行 /** * @ClassName ThreadJoinExample * @projectName: object1 * @author ...
- 一篇文章讲明白vue3的script setup,拥抱组合式API!
引言 vue3除了Composition API是一个亮点之外,尤大大又给我们带来了一个全新的玩意 -- script setup,对于setup大家相信都不陌生,而对于script setup有些同 ...
- 【剑指Offer】52. 两个链表的第一个公共节点 解题报告(Python)
作者: 负雪明烛 id: fuxuemingzhu 个人博客:http://fuxuemingzhu.cn/ 目录 题目描述 解题方法 方法一:栈 方法二:HashSet 方法三:不使用额外空间 日期 ...
- MySQL中视图的定义、原理--触发器
视图概述 视图是一个虚拟表,其内容由查询定义.同真实的表一样,视图包含一系列带有名称的列和行数据.但是,视图并不在数据库中以存储的数据值集形式存在.行和列数据来自由定义视图的查询所引用的表,并且在引用 ...
- [算法笔记-题解]问题 A: 例题4-1 一元二次方程求根
问题 A: 例题4-1 一元二次方程求根 [命题人 : 外部导入] 时间限制 : 1.000 sec 内存限制 : 12 MB 题目描述 求一元二次方程ax2+bx+c=0的根,三个系数a, b, c ...
- 教你如何6秒钟往MySQL插入100万条数据!然后删库跑路!
教你如何6秒钟往MySQL插入100万条数据!然后删库跑路! 由于我用的mysql 8版本,所以增加了Timezone,然后就可以了 前提是要自己建好库和表. 数据库test, 表user, 三个字段 ...
- Spring Boot程序接收命令行参数
Spring Boot程序接收命令行参数 输入一行,回车,触发一次.如果想要调用service层,也是可以,能调用service层,就可以做很多事,触发一次就好比调用了一次http接口一样 packa ...