前言:

当你在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. CF306C White, Black and White Again

    CF306C White, Black and White Again 洛谷评测传送门 题目描述 Polycarpus is sure that his life fits the descripti ...

  2. 洛谷 U86501 趣味擂台

    洛谷 U86501 趣味擂台 题目传送门 题目背景 \(JDFZ\)\(2019\)秋季运动会开始辣!运动会中有一个叫做"趣味擂台"的游戏...... 题目描述 游戏内容是这样的: ...

  3. ASP.NET开发实战——(三)第一个ASP.NET应用《MyBlog》

    本文开始通过ASP.NET MVC创建一个博客应用,该应用是通过默认的MVC模板修改而来,所以创建的过程和代码都与默认模板一致,然后通过修改的方式将默认模板改为博客的主页,并添加博客列表.内容等页面. ...

  4. ORACLE--报错,ORA-38770

    一,问题描述 背景说明,Dg备库需要恢复到三天前,但是期间没有开闪回,临时开始,出现了这个错误.原因分析: To enable Flashback Database, you configure a ...

  5. 开源组件websocket-sharp中基于webapi的httpserver使用体验

    一.背景 因为需要做金蝶ERP的二次开发,金蝶ERP的开放性真是不错,但是二次开发金蝶一般使用引用BOS.dll的方式,这个dll对newtonsoft.json.dll这个库是强引用,必须要用4.0 ...

  6. springcloud(七,多个服务消费者配置,以及zuul网关案例)

    spring cloud (一.服务注册demo_eureka) spring cloud (二.服务注册安全demo_eureka) spring cloud (三.服务提供者demo_provid ...

  7. 推荐一款手机清理工具App悟空清理

    推荐一款手机清理工具App悟空清理 1 介绍 悟空清理是一款完全免费的手机加速与存储空间清理工具软件,强力去除顽固垃圾,使手机运行更畅快. 2 特色功能介绍 悟空在手,清理无忧!悟空清理,人人都在用的 ...

  8. JavaScript 系列--JavaScript一些奇淫技巧的实现方法(二)数字格式化 1234567890转1,234,567,890;argruments 对象(类数组)转换成数组

    一.前言 之前写了一篇文章:JavaScript 系列--JavaScript一些奇淫技巧的实现方法(一)简短的sleep函数,获取时间戳 https://www.mwcxs.top/page/746 ...

  9. python运维开发常用模块(一)psutil

    1.模块简介 psutil是一个跨平台库(http://code.google.com/p/psutil/),能够轻 松实现获取系统运行的进程和系统利用率(包括CPU.内存.磁盘.网 络等)信息.它主 ...

  10. 《Interest Rate Risk Modeling》阅读笔记——第二章:债券价格、久期与凸性

    目录 第二章:债券价格.久期与凸性 思维导图 瞬时回报率-收益率的例子 第二章:债券价格.久期与凸性 思维导图 瞬时回报率-收益率的例子