Mysql DDL出现长时间等待MDL问题分析

彩云
• 阅读 2268

给表新增字段时,发现锁表了,查看进程,提示Waiting for table metadata lock,等待锁释放;然而蛋疼的是几分钟过去了,依然没有任何的进展,特此记录下这个问题的定位过程以及MDL的相关背景知识

看到上面的表现,基本问题就来了

  • Metadata Lock 是什么鬼
  • 是什么原因导致一直等待

<!-- more -->

I. 问题定位

首先需要确认什么地方加锁,从mysql出发,应该怎么定位?

1. 定位过程

对于mysql而言,一般来讲上锁和事物时伴生关系,所以我们的直观出发点就是查找db当前正在执行的事物

-- 查询当前正在执行的事物的sql
SELECT * FROM information_schema.INNODB_TRX;

输出结果如下,首先拿到事物对应的进程id

Mysql DDL出现长时间等待MDL问题分析

拿到id之后,则可以分析对应的进程信息

-- 查询进程信息
show processlist

-- 查询所有的进程信息
show full processlist

然后定位到具体的进程

Mysql DDL出现长时间等待MDL问题分析

然后登陆到目标机器,查看端口号对应的进程,通过lsof命令查看

lsof -i tcp:52951

从图中可以看出,是一个python进程的mysql连接开启的事物,进程id为5436

Mysql DDL出现长时间等待MDL问题分析

接着查看进程对应的信息

ps aux | grep 5436

Mysql DDL出现长时间等待MDL问题分析

这个脚本正是测试aiomysql的python脚本,内容比较简单

import asyncio
import aiomysql

loop = asyncio.get_event_loop()


@asyncio.coroutine
def test_example():
    conn = yield from aiomysql.connect(host='127.0.0.1', port=3306,
                                       user='root', password='', db='test',
                                       loop=loop, autocommit=False)

    cur = yield from conn.cursor()
    yield from cur.execute("SELECT * from test_table")
    print(cur.description)
    r = yield from cur.fetchall()
    print(r)
    yield from cur.close()
    conn.close()

loop.run_until_complete(test_example())

2. 原因分析

对python不太熟,直接借助google查一下,发现有同样的问题

  • [Why aiomysql locks the table even when using context manager?

](https://stackoverflow.com/que...

这个问题抛出,在通过with打开连接获取游标后,执行mysql,但是没有commit之前,会锁表,这个期间修改表都会出现等待

下面近给出了解答,并没有看到更多的深层次的说明,先记录下,解决办法就是在创建连接池的时候,选择自动提交方式,然后就不会有这个问题了

pool = await aiomysql.create_pool(
    host="localhost",
    user="test",
    password="test",
    db="test",
    autocommit=True,
    cursorclass=DictCursor,
    loop=loop)

II. Metadata Lock说明

找到一篇文章说MDL的,推荐详细阅读 MySQL表结构变更你不可不知的Metadata Lock详解

1. MDL 说明

抓一下核心的要点,简单说一下看完这篇文章之后的朴素理解

MetaData Lock 简称为MDL,简单来说就是表的元数据锁;当修改表结构的时候,就需要持有这个锁

a. 作用

MDL的主要作用只有一点,保护一个正在执行的事物表结构不被修改

有一个原则,MDL是事物级别的,只有事物结束之后才会释放,而这里面说的事物分为两类

  • 显示事物:

    • 关闭autocommit
    • 以begin或start transaction开始的操作
  • AC-NL-RO(auto-commit non-locking read-only):

    • auto commit 开启之下的select操作

b. 实例说明

直接看上面的说明,不太直观,一个经典的case如下

Mysql DDL出现长时间等待MDL问题分析

session1 开启了一个事物,执行查询操作;但是现在session2 要删除表,如果执行成功,那么session1的第二次查询就跪了,这样就违背了事物的原则,所有在5.5版本引入了MDL,来保证在事物执行期间,表结构不被修改

2. 出现MDL等待原因及解决方法

当我们出现修改表结构,就需要获取MDL的排他锁,因此只有这个表没有事物在执行时,才能获取成功;当持有独占锁之后,这个表的其他操作将被阻塞(即不能插入数据,修改数据,也不能开启事物操作)

因此在执行DDL时,一直出现等待MDL的时候,常见的原因有下面三个

a. 长事物,阻塞DDL,从而阻塞所有同表的后续操作

通过 show processlist看到表上有正在进行的操作(包括读),此时修改表时也会等待获取MDL,这种时候解决办法要么就是等待执行完毕,要么就是直接kill掉进程

b. 未提交事物,阻塞DDL

通过 show processlist没有找到表上的操作,但是通过information_schema.innodb_trx发现有未提交的事物,

c. 异常的状况

通过 show processlist 和事物查询都没有的情况下,可能的场景是一个显示的事物中,对表的操作出现了异常,虽然事物失败,但是持有的锁还没有释放,也会导致这个原因

可以在performance_schema.events_statements_current表中查询失败的语句

3. MDL分类与sql实例

前面两小节,分别说明什么是MDL(朴素理解为表的元数据锁),以及当修改表时出现长时间的等待MDL的原因分析;正常看完之后,应该会有下面的疑惑

  • MDL有哪些类型
  • 哪些sql会持有MDL

对于MDL的类型,从网上截一张图

Mysql DDL出现长时间等待MDL问题分析

接下来需要分析下不同锁模式对应的sql

属性 含义 事例
MDL_INTENTION_EXCLUSIVE(IX) 意向排他锁用于global和commit的加锁。 truncate table t1; insert into t1 values(3,’abcde’); 会加如下锁 (GLOBAL,MDL_STATEMENT,MDL_INTENTION_EXCLUSIVE)(SCHEMA,MDL_TRANSACTION,MDL_INTENTION_EXCLUSIVE)
MDL_SHARED(S) 只访问元数据 比如表结构,不访问数据。 set golbal_read_only =on 加锁 (GLOBAL,MDL_EXPLICIT,MDL_SHARED)
MDL_SHARED_HIGH_PRIO(SH) 用于访问information_scheam表,不涉及数据。 select * from information_schema.tables;show create table xx; desc xxx; 会加如下锁: (TABLE,MDL_TRANSACTION,MDL_SHARED_HIGH_PRIO)
MDL_SHARED_READ(SR) 访问表结构并且读表数据 select * from t1; lock table t1 read; 会加如下锁: (TABLE,MDL_TRANSACTION,MDL_SHARE_READ)
MDL_SHARED_WRITE(SW) 访问表结构并且写表数据 insert/update/delete/select .. for update 会加如下锁:(TABLE,MDL_TRANSACTION,MDL_SHARE_WRITE)
MDL_SHARED_UPGRADABLE(SU) 是mysql5.6引入的新的metadata lock,可以说是为了online ddl 才引入的。特点是允许DML,防止DDL; alter table/create index/drop index 会加该锁; 加入下锁 (TABLE,MDL_TRANSACTION,MDL_SHARED_UPGRADABLE)
MDL_SHARED_NO_WRITE(SNW) 可升级锁,访问表结构并且读写表数据,并且禁止其它事务写。 alter table t1 modify c bigint; (非onlineddl) (TABLE,MDL_TRANSACTION,MDL_SHARED_NO_WRITE)
MDL_SHARED_NO_READ_WRITE(SNRW) 可升级锁,访问表结构并且读写表数据,并且禁止其它事务读写。 lock table t1 write; 加锁 (TABLE,MDL_TRANSACTION,MDL_SHARED_NO_READ_WRITE
MDL_EXCLUSIVE(X) 防止其他线程读写元数据 CREATE/DROP/RENAME TABLE,其他online DDL在rename阶段也持有X锁(TABLE,MDL_TRANSACTION,MDL_EXCLUSIVE)

4, 小结

上面的内容,可能信息量比较大,特别是MDL的锁分类情况,很难抓住重点,针对我们日常接触中,简单给出小结

  • MDL是为了保证事物执行过程中,表结构不被修改引入的;因此修改表结构的前提是这个表上没有事物(没有正在执行,失败,或者未提交的事物)
  • DDL执行,一般来讲是需要获取排他的MDL
  • DML都会开启事物,因此会获取 MDL_SW
  • DQL语句会获取 MDL_SR

几个简称的说明

  • MDL: metadata lock,可以简单理解为表的元数据锁
  • DDL: 数据定义语言,可以简单理解为表的操作,如创建,修改,删除表、视图等,新增索引、字段等操作
  • DML: 数据操作语言,也就是我们常规理解的 insert, update, delete 语句
  • DQL: 数据查询语言,常见的select语句

几个常见疑问解答

a. 为什么同一张表的多个DDL不能并行执行

  • MDL读锁是互相兼容的,可以有多个增删查改
  • MDL写锁是互斥的,只能有一个表的DDL

b. 为什么有时候DDL会卡住

  • MDL读写锁之间是互斥的,所以如果DDL卡住,就证明有事务在执行,不能申请MDL写锁

c. 常见卡住的场景

  • 非常频繁的业务高峰期
  • 有慢查询把持着MDL读锁
  • 有事物一直未提交

d. 为什么需要MDL锁

  • 当事务本身执行的时候理论上是不能容忍表结构在中途发生改变的

5. 更多参考

相关博文或者问答

  • [Why aiomysql locks the table even when using context manager?

](https://stackoverflow.com/que...

II. 其他

1. 一灰灰Bloghttps://liuyueyi.github.io/he...

一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛

2. 声明

尽信书则不如,已上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激

3. 扫描关注

一灰灰blog

Mysql DDL出现长时间等待MDL问题分析

知识星球

Mysql DDL出现长时间等待MDL问题分析

点赞
收藏
评论区
推荐文章
Wesley13 Wesley13
4年前
java中的锁
记录一下公平锁,非公平锁,可重入锁(递归锁),读写锁,自旋锁的概念,以及一些和锁有关的java类。公平锁与非公平锁:公平锁就是在多线程环境下,每个线程在获取锁时,先查看这个锁维护的队列,如果队列为空或者自身就是等待队列的第一个,就占有锁。否则就加入到等待队列中,按照FIFO的顺序依次占有锁。非公平锁会一上来就试图占
CuterCorley CuterCorley
4年前
数据库编程 MySQL 技巧与经验
1.MySQL创建数据表时设定引擎并添加外键约束创建两个数据表,在它们之间添加外键约束,然后在被添加外键的表中添加数据,发现并没有提示报错,很正常地插入了数据,说明外键没有添加成功,在SQL可视化工具里查看表的属性,并点击外部键会出现弹窗,提示是因为引擎的问题,导致不能添加外键。MySQL安装时默认用的表引擎是MyISAM,而MyISAM是不支持外键的,如
Wesley13 Wesley13
4年前
MySQL 空事务
   问题描述;   研发同事反应MySQL数据库有锁,检查innodb\_trx时,发现有很多长时间未结束的空事务。   这些事务的trx\_mysql\_thread\_id都为0,因此不能通过kill  id的方式强制关闭这些长时间未结束的僵尸事务。           SELECT       tr
Wesley13 Wesley13
4年前
Java并发包小结
1、Lock  Lock功能对应关键字synchrozied功能,lock和unlock方法用于加锁和释放锁。等待锁的线程加入到等待链表中,同时阻塞线程,锁释放时,从等待链表中取出等待的线程执行,取等待的线程分公平与非公平两种方式,公平方式取第一个等待的线程,非公平方式当前正在获取锁的线程可能立刻执行,而不用加入到等待队列中,排队执行。2、Con
Stella981 Stella981
4年前
Innodb中mysql如何快速删除2T的大表
这个时候所有的mysql的相关进程都会停止,直到drop结束,mysql才会恢复执行。出现这个情况的原因就是因为,在droptable的时候,innodb维护了一个全局锁,drop完毕锁就释放了。这意味着,如果在白天,访问量非常大的时候,如果你在不做任何处理措施的情况下,执行了删大表的命令,整个mysql就挂在那了,在删表期间,QPS会严重下滑,然后产
Wesley13 Wesley13
4年前
RDS MySQL InnoDB 锁等待和锁等待超时的处理
https://help.aliyun.com/knowledge\_detail/41705.html1\.Innodb引擎表行锁等待和等待超时发生的场景(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fhelp.aliyun.com%2Fknowledge_detail%2F
Wesley13 Wesley13
4年前
Mysql锁总结
今天我们来介绍一下Mysql中不同类型的锁数据库锁设计的初衷是处理并发问题。作为多用户共享的资源,当出现并发访问的时候,数据库需要合理地控制资源的访问规则。而锁就是用来实现这些访问规则的重要数据结构根据加锁的范围,MySQL里面的锁大致可以分成全局锁、表级锁和行锁三类全局锁全局锁就是对整个数据库实例加锁。MySQL提供了一个加全
Wesley13 Wesley13
4年前
TMS32F28027的GPIO34驱动光耦问题
问题:硬件研发同事在调试一块TMS32F28027产品电路板时发现GPIO34出现上电控制正常,运行中只要Reset芯片,系统进入锁死状态,GPIO34引脚外部直接通过一个510欧电阻驱动光耦TLP181。据同事描述只有这个引脚驱动光耦的时候会出现这个问题,换用其它引脚驱动光耦,或则光耦去掉换成驱动LED都不会发生这个问题。相关原理图如下:!(h
Wesley13 Wesley13
4年前
mysql出现Waiting for table metadata lock的解决方法
查询某一个表时,一直没有显示数据,于是就showprocesslist;!(https://oscimg.oschina.net/oscnet/07f86cfca979bba85837f1b6352eb00ec33.jpg)发现有表已经被锁了,关掉了之前的查询语句可以看到!(https://oscimg.oschina.net/oscn
你还在为SFTP连接超时而困惑么? | 京东云技术团队
1\.前言在最近的项目联调过程中,发现在连接上游侧SFTP时总是需要等待大约10s的时间才会出现密码输入界面,这种长时间的等待直接导致的调用文件接口时连接sftp超时问题。于是决定自己针对该问题进行一下排查,查询了相关资料,并逐个试验了一下网上提供的解决
线上SQL超时场景分析-MySQL超时之间隙锁 | 京东物流技术团队
前言之前遇到过一个由MySQL间隙锁引发线上sql执行超时的场景,记录一下。背景说明分布式事务消息表:业务上使用消息表的方式,依赖本地事务,实现了一套分布式事务方案消息表名:mqmessages数据量:3000多万索引:createtime和statuss