前言:

当你在MySQL中执行一条SQL时,语句并没有在你预期的时间内执行完成,这时候我们通常会登陆到MySQL数据库上查看是不是出了什么问题,通常会使用的一个命令就是 show processlist,看看有哪些session,这些session在做什么事情。当你看到 waiting for table metadata lock 时,那就是遇到MDL元数据锁了。本篇文章将会介绍MDL锁的产生与排查过程。

1.什么是MDL锁

MDL全称为metadata lock,即元数据锁。MDL锁主要作用是维护表元数据的数据一致性,在表上有活动事务(显式或隐式)的时候,不可以对元数据进行写入操作。因此从MySQL5.5版本开始引入了MDL锁,来保护表的元数据信息,用于解决或者保证DDL操作与DML操作之间的一致性。

对于引入MDL,其主要解决了2个问题,一个是事务隔离问题,比如在可重复隔离级别下,会话A在2次查询期间,会话B对表结构做了修改,两次查询结果就会不一致,无法满足可重复读的要求;另外一个是数据复制的问题,比如会话A执行了多条更新语句期间,另外一个会话B做了表结构变更并且先提交,就会导致slave在重做时,先重做alter,再重做update时就会出现复制错误的现象。

元数据锁是server层的锁,表级锁,每执行一条DML、DDL语句时都会申请MDL锁,DML操作需要MDL读锁,DDL操作需要MDL写锁(MDL加锁过程是系统自动控制,无法直接干预,读读共享,读写互斥,写写互斥),申请MDL锁的操作会形成一个队列,队列中写锁获取优先级高于读锁。一旦出现写锁等待,不但当前操作会被阻塞,同时还会阻塞后续该表的所有操作。事务一旦申请到MDL锁后,直到事务执行完才会将锁释放。(这里有种特殊情况如果事务中包含DDL操作,mysql会在DDL操作语句执行前,隐式提交commit,以保证该DDL语句操作作为一个单独的事务存在,同时也保证元数据排他锁的释放)。

注: 支持事务的InnoDB引擎表和不支持事务的MyISAM引擎表,都会出现Metadata Lock Wait等待现象。一旦出现Metadata Lock Wait等待现象,后续所有对该表的访问都会阻塞在该等待上,导致连接堆积,业务受影响。

2.模拟与查找MDL锁

MDL锁通常发生在DDL操作挂起的时候,原因是有未提交的事务对该表进行DML操作。而MySQL的会话那么多,不知道哪个会话的操作没有及时提交影响了DDL。通常我们排查这类问题,往往需要从information_schema.innodb_trx表中查询当前在执行的事务,但当SQL已经执行过了,没有commit,这个时候这个表中是看不到SQL的。

在MySQL5.7中,performance_schema库中新增了metadata_locks表,专门记录MDL的相关信息。首先要开启MDL锁记录,执行如下SQL开启:

UPDATE performance_schema.setup_instruments
SET ENABLED = 'YES', TIMED = 'YES'
WHERE NAME = 'wait/lock/metadata/sql/mdl';

下面展示下模拟及查找MDL锁的过程:

# 会话1 事务中执行DML操作
mysql> begin;
Query OK, 0 rows affected (0.00 sec) mysql> insert into student_tb (stu_id,stu_name) values (1009,'xin');
Query OK, 1 row affected (0.00 sec) mysql> select * from student_tb;
+--------------+--------+----------+---------------------+---------------------+
| increment_id | stu_id | stu_name | create_time | update_time |
+--------------+--------+----------+---------------------+---------------------+
| 1 | 1001 | from1 | 2019-11-28 16:36:14 | 2019-11-28 16:36:14 |
| 2 | 1002 | dfsfd | 2019-11-28 16:36:14 | 2019-11-28 16:36:14 |
| 3 | 1003 | fdgfg | 2019-11-28 16:36:14 | 2019-11-28 16:36:14 |
| 4 | 1004 | sdfsdf | 2019-11-28 16:36:14 | 2019-11-28 16:36:14 |
| 5 | 1005 | dsfsdg | 2019-11-28 16:36:14 | 2019-11-28 16:36:14 |
| 6 | 1006 | fgd | 2019-11-28 16:36:14 | 2019-11-28 16:36:14 |
| 7 | 1007 | fgds | 2019-11-28 16:36:14 | 2019-11-28 16:36:14 |
| 8 | 1008 | dgfsa | 2019-11-28 16:36:14 | 2019-11-28 16:36:14 |
| 9 | 1009 | xin | 2019-11-28 17:05:29 | 2019-11-28 17:05:29 |
+--------------+--------+----------+---------------------+---------------------+ # 会话2 对该表加字段 执行DDL操作 发现DDL挂起
mysql> alter table student_tb add stu_age int after stu_name; # 会话3 查询所有会话 发现发生MDL锁
mysql> show processlist;
+----+------+-----------+--------+---------+------+---------------------------------+-------------------------------------------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----+------+-----------+--------+---------+------+---------------------------------+-------------------------------------------------------+
| 31 | root | localhost | testdb | Sleep | 125 | | NULL |
| 32 | root | localhost | testdb | Query | 7 | Waiting for table metadata lock | alter table student_tb add stu_age int after stu_name |
| 33 | root | localhost | testdb | Query | 0 | starting | show processlist |
+----+------+-----------+--------+---------+------+---------------------------------+-------------------------------------------------------+ # 会话3 查看metadata_locks表记录 发现student_tb表有MDL锁冲突
mysql> select * from performance_schema.metadata_locks;
+-------------+--------------------+----------------+-----------------------+---------------------+---------------+-------------+--------+-----------------+----------------+
| OBJECT_TYPE | OBJECT_SCHEMA | OBJECT_NAME | OBJECT_INSTANCE_BEGIN | LOCK_TYPE | LOCK_DURATION | LOCK_STATUS | SOURCE | OWNER_THREAD_ID | OWNER_EVENT_ID |
+-------------+--------------------+----------------+-----------------------+---------------------+---------------+-------------+--------+-----------------+----------------+
| TABLE | testdb | student_tb | 94189250717664 | SHARED_WRITE | TRANSACTION | GRANTED | | 56 | 34 |
| GLOBAL | NULL | NULL | 139764477045472 | INTENTION_EXCLUSIVE | STATEMENT | GRANTED | | 57 | 18 |
| SCHEMA | testdb | NULL | 139764477697808 | INTENTION_EXCLUSIVE | TRANSACTION | GRANTED | | 57 | 18 |
| TABLE | testdb | student_tb | 139764477697904 | SHARED_UPGRADABLE | TRANSACTION | GRANTED | | 57 | 18 |
| TABLE | testdb | student_tb | 139764477697696 | EXCLUSIVE | TRANSACTION | PENDING | | 57 | 18 |
| TABLE | performance_schema | metadata_locks | 139764544135120 | SHARED_READ | TRANSACTION | GRANTED | | 58 | 20 |
+-------------+--------------------+----------------+-----------------------+---------------------+---------------+-------------+--------+-----------------+----------------+ # 会话3 联合其他系统表 查找出会话ID
mysql> select m.*,t.PROCESSLIST_ID from performance_schema.metadata_locks m left join performance_schema.threads t on m.owner_thread_id=t.thread_id;
+-------------+--------------------+----------------+-----------------------+---------------------+---------------+-------------+--------+-----------------+----------------+----------------+
| OBJECT_TYPE | OBJECT_SCHEMA | OBJECT_NAME | OBJECT_INSTANCE_BEGIN | LOCK_TYPE | LOCK_DURATION | LOCK_STATUS | SOURCE | OWNER_THREAD_ID | OWNER_EVENT_ID | PROCESSLIST_ID |
+-------------+--------------------+----------------+-----------------------+---------------------+---------------+-------------+--------+-----------------+----------------+----------------+
| TABLE | testdb | student_tb | 94189250717664 | SHARED_WRITE | TRANSACTION | GRANTED | | 56 | 34 | 31 |
| GLOBAL | NULL | NULL | 139764477045472 | INTENTION_EXCLUSIVE | STATEMENT | GRANTED | | 57 | 18 | 32 |
| SCHEMA | testdb | NULL | 139764477697808 | INTENTION_EXCLUSIVE | TRANSACTION | GRANTED | | 57 | 18 | 32 |
| TABLE | testdb | student_tb | 139764477697904 | SHARED_UPGRADABLE | TRANSACTION | GRANTED | | 57 | 18 | 32 |
| TABLE | testdb | student_tb | 139764477697696 | EXCLUSIVE | TRANSACTION | PENDING | | 57 | 18 | 32 |
| TABLE | performance_schema | metadata_locks | 139764544135120 | SHARED_READ | TRANSACTION | GRANTED | | 58 | 22 | 33 |
| TABLE | performance_schema | threads | 139764549217280 | SHARED_READ | TRANSACTION | GRANTED | | 58 | 22 | 33 |
+-------------+--------------------+----------------+-----------------------+---------------------+---------------+-------------+--------+-----------------+----------------+----------------+ # 结果解读:从上面结果明显可以看出会话31持有student_tb表的SHARED_WRITE锁,
# 需要等待其提交后或手动杀掉该会话方可解除MDL锁。

3.如何优化与避免MDL锁

MDL锁一旦发生会对业务造成极大影响,因为后续所有对该表的访问都会被阻塞,造成连接积压。我们日常要尽量避免MDL锁的发生,下面给出几点优化建议可供参考:

  • 开启metadata_locks表记录MDL锁。
  • 设置参数lock_wait_timeout为较小值,使被阻塞端主动停止。
  • 规范使用事务,及时提交事务,避免使用大事务。
  • 增强监控告警,及时发现MDL锁。
  • DDL操作及备份操作放在业务低峰期执行。
  • 少用工具开启事务进行查询,图形化工具要及时关闭。

总结:

本篇文章主要分三方面来详解MDL锁,首先介绍了MDL锁产生的原因及作用,然后我们模拟出MDL锁,并给出查找及解决方法,最后给出几点避免MDL锁的建议。其实,MDL锁在DB运维过程中经常遇到,它不是洪水猛兽,只是为了保护数据库对象,保证数据一致性。希望大家看完这篇文章后能对MDL锁有更清晰的认识。

参考:

深入理解MDL元数据锁的更多相关文章

  1. (转载)深入理解MDL元数据锁

    作者:MySQL技术本文为作者原创,转载请注明出处:https://www.cnblogs.com/kunjian/p/11993708.html 前言: 当你在MySQL中执行一条SQL时,语句并没 ...

  2. 深入理解MYSQL的MDL元数据锁

    1 前言 2 MDL锁与实现 3 MDL锁的性能与并发改进 4 MDL锁的诊断 前言 好久没更新,主要是因为Inside君最近沉迷于一部动画片——<新葫芦娃兄弟>.终于抽得闲,完成了本篇关 ...

  3. 一步步搞懂MySQL元数据锁(MDL)

    某日,路上收到用户咨询,为了清除空间,想删除某200多G大表数据,且已经确认此表不再有业务访问,于是执行了一条命令'delete from bigtable',但好长时间也没删完,经过咨询后,获知dr ...

  4. MDL--元数据锁的锁请求与锁等待+元数据锁类对象

    1 元数据锁的锁请求与锁等待     元数据锁在MySQL Server层,依照锁的状态被细分为两种.一种是已经施加的锁.一种是等待施加的锁即锁请求,这样被区分的原因,如MySQL对"cla ...

  5. Mysql加锁过程详解(8)-理解innodb的锁(record,gap,Next-Key lock)

    Mysql加锁过程详解(1)-基本知识 Mysql加锁过程详解(2)-关于mysql 幻读理解 Mysql加锁过程详解(3)-关于mysql 幻读理解 Mysql加锁过程详解(4)-select fo ...

  6. SQLSERVER中的元数据锁

    SQLSERVER中的元数据锁 网上对于元数据锁的资料真的非常少 元数据锁一般会出现在DDL语句里 下面列出数据库引擎可以锁定的资源 资源 说明 RID 用于锁定堆(heap)中的某一行 KEY 用于 ...

  7. 理解MySql的锁&事务隔离级别

    这几篇文章是从网上(http://www.hollischuang.com)看到的一系列文章,也是重温了一下数据库的相关知识.下面是对这些文章的一些前后行文逻辑的说明: 我们知道,在DBMS的多个事业 ...

  8. 一分钟理解Java公平锁与非公平锁

    本人免费整理了Java高级资料,涵盖了Java.Redis.MongoDB.MySQL.Zookeeper.Spring Cloud.Dubbo高并发分布式等教程,一共30G,需要自己领取.传送门:h ...

  9. 深入理解 Java 并发锁

    本文以及示例源码已归档在 javacore 一.并发锁简介 确保线程安全最常见的做法是利用锁机制(Lock.sychronized)来对共享数据做互斥同步,这样在同一个时刻,只有一个线程可以执行某个方 ...

随机推荐

  1. JS中的箭头函数与this

    转载自:https://juejin.im/post/5aa1eb056fb9a028b77a66fd#heading-1 JavaScript在ES6语法中新增了箭头函数,相较于传统函数,箭头函数不 ...

  2. pcp 基本使用

    pcp 是一款强大的metrics 分析工具,包含了比较完整的指标分析工具链,同时也已经包含了好多pmdas 核心组件 参考架构 安装 centos yum install -y pcp 启动 sys ...

  3. treegrid 表格树

    treegrid  实现表格树的结构 效果图: 第一步:页面布局 <div class="col-sm-12 select-table table-striped" styl ...

  4. [LeetCode] 305. Number of Islands II 岛屿的数量之二

    A 2d grid map of m rows and n columns is initially filled with water. We may perform an addLand oper ...

  5. [LeetCode] 189. Rotate Array 旋转数组

    Given an array, rotate the array to the right by k steps, where k is non-negative. Example 1: Input: ...

  6. 分布式事物解决方案-TCC

    分布式框架下,如何保证事物一致性一直是一个热门话题.当然事物一致性解决方案有很多种(请参考:分布式事物一致性设计思路),我们今天主要介绍TCC方案解决的思路.以下是参与设计讨论的一种解决思路,大家有问 ...

  7. Shell脚本——求随机数的最值

    写一个脚本,利用RANDOM生成10个随机数,并找出其中的最大值,和最小值: #!/bin/bash # MAX= MIN= ..};do RAN=$RANDOM [ $i -eq ] &&a ...

  8. 创建简单Maven项目

    目录: Maven基础构建概念.仓库.构建与部署 Maven作用 Maven项目install Maven安装配置.目录结构.配置文件 配置Maven默认本地仓库 Maven常见命令 使用Maven ...

  9. 标签一致项(LC-KSVD)-全文解读

    Learning A Discriminative Dictionary for Sparse Coding via Label Consistent K-SVD 1,同步学习判决字典和线性分类器 2 ...

  10. linux centos安装教程

    linux centos安装教程1 CentOS-7-x86_64-DVD-1511.iso 这个是dvd版本 2 CentOS-7-x86_64-Minimal-1511.iso 这个迷你版 是没有 ...