【MySQL经典案例分析】 Waiting for table metadata lock

一、 问题是这样来的

     2018年某个周末,接到连续数据库的告警,告警信息如下:

二、 苦逼的探索过程

1、总体的思路

  看到too many connection的报错信息,基本上可以把问题定位在:

(1)机器负载飙升,导致SQL执行效率下降,导致连接推积

(2)业务访问量突增(或者有SQL注入现象),导致连接数打满

(3)出现“死锁”或者锁竞争严重,导致大量SQL堆积

2、排查过程

(1)机器的各项性能指标都显示正常, 没有出现高负载现象,暂时先排除了这种原因

(2)查看监控信息,发现在连接数打满的时间点前并没有访问量突增的趋势,同时通过检查告警信息并没有发现有注入工单

(3)最后上到服务器上查看下SQL的执行情况

①查看show full processlist;

      大量的请求都是在“Waiting for table metadata lock”,可以分成三类请求:

  • Select请求
  • Rename请求
  • Sleep请求

②分析Waiting for table metadata lock

       一般来说常见的“Waiting for table metadata lock”会出现在DDL操作或者是有未提交的事务上,从information_schema.processlist表中,没有发现有DDL操作,而能够产生MDL锁的操作也只剩下rename,但是根据SQL执行的状态,rename操作也是在等待MDL锁,所以rename操作应该是被阻塞的操作,而不是产生MDL锁的操作。

     接着我们来查看下死锁和事务的相关指标:

  •   show engine innodb status;中没有任何死锁的信息
  •   information_schema.innodb_trx 、information_schema.innodb_locks 、 information_schema.innodb_lock_waits 的也没有任何形式的锁信息。

      现在基本又排除了显示的死锁问题,那是从show full processlist中也抓不出任何请求,这里就比较疑惑了,当看了下表的结构式,发现这个表是myisam引擎的,所以上面的两种统计信息里面没有任何值就可以解释了。

      那么其实问题就集中在有未结束的事务上了,这里其实有一个误区,当时跟开发沟通存在未关闭的事务时,开发一直认为不可能,因为myisam表是不支持事务的,只有innodb支持事务。但是对于MDL锁来说,5.5之后引入MDL事务级别的锁不论对myisam还是innodb都是生效的。

③查看未提交的事务

      之后查看了下系统的事务自动提交的变量,autocommit的值是ON,那说明如果是事务未提交的话只可能是业务主动的开启一个事务,而没有commit。

      为了验证这个猜想,打开了general log,在log中果然发现,业务在开启事务后,把autocommit的值设为0了,导致必须要显示的commit才能提交事务。

这时候我们反过头来看一下host为10.49.84.70的连接请求,由于select的执行速度很快,而且访问并不频繁,所以在抽样的show processlist中,状态值大部分时间是“Sleep”,给问题的定位带来了一些迷惑性的干扰。接着我们kill掉了这个进程,果然推积的请求瞬间就执行完成了,也之间印证了刚刚上述推论。

2、问题解决

       在与开发同学沟通过程中,开发同学说库中是myisam表所以不会主动开启事务,在代码里也没有设置autocommit=0的代码,那么根本原因在哪?

       当我们定位到这台服务器上的请求都是来自python的定时脚本,使用python 操作mysql的时候,使用了其pymysql模块,但是在进行插入操作的时候,必须使用受到提交事务。Python的pymysql模块默认是会设置autocommit=0的。

      让我们来对比一下其他同样使用python访问的正常连接请求,再断开前都会手动的commit。

找到原因后有思考了下,是不是可以在建连后就设置autocommit=1呢?这样对于之后新变更的SQL就不要再考虑到手动commit的事情了,可以通过在初始化连接池的时候,对每一个连接进行设置,即

三、 延伸的一些思考

  1、metadata lock

(1)MDL简述

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

  对于引入MDL,其主要解决了2个问题,一个是事务隔离问题,比如在可重复隔离级别下,会话A在2次查询期间,会话B对表结构做了修改,两次查询结果就会不一致,无法满足可重复读的要求;另外一个是数据复制的问题,比如会话A执行了多条更新语句期间,另外一个会话B做了表结构变更并且先提交,就会导致slave在重做时,先重做alter,再重做update时就会出现复制错误的现象。所以在对表进行上述操作时,如果表上有活动事务(未提交或回滚),请求写入的会话会等待在Metadata lock wait 。

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

(2)常见MDL锁场景

①当前有执行DML操作时执行DDL操作

② 当前有对表的长时间查询或使用mysqldump/mysqlpump时,使用alter会被堵住

③ 显示或者隐式开启事务后未提交或回滚,比如查询完成后未提交或者回滚,DDL会被堵住

④ 表上有失败的查询事务,比如查询不存在的列,语句失败返回,但是事务没有提交,此时DDL仍然会被堵住

2、myisam、innodb对事务的支持

       Myisam是不支持事务的,innodb是支持事务的,这个概念其实没有任何问题,但是这里只的都是对于数据的事务性操作的支持,通过如下简单的实验可以很清楚的理解(关于事务的相关概念和解释就不再赘述了,只是想区别一下mysiam不支持事务,但是主动开始事务中对Myisam的操作仍然会产生MDL锁):

      在隔离级别为RC的情况下:

(1)myisam表

① CREATE TABLE `tb2` (`a` int(11) DEFAULT NULL ) ENGINE=MyISAM;

② Session 1:

       mysql> begin ;

      mysql> insert into tb2(a) value(1);

    (在session2的update之后)

      mysql> select * from tb2;

         +--------+

         |    a     |

        +--------+

         |    3     |

        +--------+

  Session 2:

      mysql> select * from tb2;

        +---------+

        |    a      |

        +---------+

       |    1       |

       +---------+

    mysql> update tb2 set a=3 where a=1;

    mysql> select * from tb2;

       +--------+

        |     a    |

        +--------+

        |    3      |

        +--------+

     mysql> alter table tb2 add b int(11);

... hangs ...

(2)innodb表

①CREATE TABLE `tb3` (`a` int(11) DEFAULT NULL ) ENGINE=INNODB;

② Session 1:

      mysql> begin ;

      mysql> insert into tb3(a) value(1);

    Session 2:

       mysql> select * from tb3;

      Empty set (0.00 sec)

3、myisam表的另一个BUG

(1)场景

① CREATE TABLE `tb2` (`a` int(11) DEFAULT NULL ) ENGINE=MyISAM;

② Session 1:

        mysql> begin ;

        mysql> select * from tb2;

    Session 2:

       mysql> create table if not exists tb2(a int);

   ... hangs ...

 ③查看show processlist

    Session 1:Sleep

    Session 2:Waiting for table metadata lock

(2)解决方式

 ①session 1上commit或者rollback

 ②另外再开一个session3 ,kill掉可疑连接

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏沃趣科技

初相识 | 全方位认识 sys 系统库

前阵子,我们的"全方位认识performance_schema"系列为大家完整的介绍了performance_schema系统库。在我们的发布计划中为什么要把p...

23730
来自专栏杨建荣的学习笔记

一则orabbix报警的分析(r6笔记第65天)

最近使用zabbix监控之后,都会在凌晨收到1台数据库服务器的报警短信,报警的内容为: No data received from Orabbix 这个错误其实...

33380
来自专栏数据和云

腾讯游戏DBA利刃 - SQL审核工具介绍

作者介绍 ? 韩全安(willhan) 华中科技大学,硕士,现代数据库方向。2013年毕业,就职于腾讯到今,工作项目:TMySQL、SQL审核、InnoDB列压...

1.4K60
来自专栏数据和云

Library Cache优化与SQL游标

? 冷菠 冷菠,网名悠然(个人主页http://www.orasky.net),资深DBA,著有《Oracle高性能自动化运维》,有近10年的数据库运维、团队...

29150
来自专栏智能大石头

每天4亿行SQLite订单大数据测试(源码)

SQLite单表4亿订单,大数据测试 SQLite作为嵌入式数据库的翘楚,广受欢迎! 新生命团队自2010年以来,投入大量精力对SQLite进行学习研究,成功应...

99560
来自专栏数据和云

细致入微:Oracle中执行计划在Shared Pool中的存储位置探秘

这两天我一直在想一个问题,那就是 Oracle 的执行计划到底存储在什么地儿?它会是一种什么样的格式? 这里我试图对这个问题做一点我自己认为的解释,这个解释可能...

31550
来自专栏MongoDB中文社区

MongoDB事务模型分析

在了解写操作的事务性之前,需要先了解mongo层的每一个table,是如何与wiredtiger层的table(btree)对应的。mongo层一个最简单的ta...

31220
来自专栏idba

死锁案例之十

死锁,其实是一个很有意思也很有挑战的技术问题,大概每个DBA和部分开发同学都会在工作过程中遇见 。关于死锁我会持续写一个系列的案例分析,希望能够对想了解死锁的朋...

11120
来自专栏数据和云

忘记SQL Server 管理员密码不可怕,学会这招就够了

作者 | 邹建,资深数据库专家,精通各项 SQL Server 技术,具有丰富的管理、维护、优化能力以及业务应用经验。他一直热心于技术知识的分享、传播,持续活跃...

24930
来自专栏PHP在线

MYSQL性能优化分享(分库分表)

MYSQL性能优化之分库分表与不停机修改mysql表结构,需要的朋友可以参考下 1、分库分表 很明显,一个主表(也就是很重要的表,例如用户表)无限制的增长势必严...

41050

扫码关注云+社区

领取腾讯云代金券