本文由云+社区发表

一、 问题是这样来的

​ 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掉可疑连接

此文已由作者授权腾讯云+社区发布

搜索关注公众号「云加社区」,第一时间获取技术干货,关注后回复1024 送你一份技术课程大礼包!

【MySQL经典案例分析】 Waiting for table metadata lock的更多相关文章

  1. 【MySQL】DDL因Waiting for table metadata lock卡住

    在数据库空闲时间,对表做碎片整理: alter table my_abc engine=innodb; 发现会话被阻塞,显示状态是: Waiting for table metadata lock 手 ...

  2. 【转】【MySql】Waiting for table metadata lock原因分析

    MySQL在进行alter table等DDL操作时,有时会出现Waiting for table metadata lock的等待场景.而且,一旦alter table TableA的操作停滞在Wa ...

  3. MySQL Waiting for table metadata lock的解决方法

    最近需要在某一个表中新增字段,使用Sequel Pro 或者Navicat工具都会出现程序没有反应,使用 show processlist 查看,满屏都是 Waiting for table meta ...

  4. 记一次MySQL中Waiting for table metadata lock的解决方法

    最近项目中的数据库查询经常挂起,应用程序启动后也报操作超时.测试人员就说数据库又挂了(貌似他们眼中的连接失败,查询无果都是挂了),通过 show processlist 一看,满屏都是 Waiting ...

  5. 记一次MySQL出现Waiting for table metadata lock的原因、排查过程与解决方法

    任务背景:将sql文件通过shell直接导入到mysql中执行(还原) bug表现:导入后java项目卡死 过程: 1.网上乱搜一通,无意间看到一篇文章,这篇文章说明了如何开启mysql的genera ...

  6. MySQL出现Waiting for table metadata lock的原因以及解决方法

    转自:http://ctripmysqldba.iteye.com/blog/1938150 (有修改) MySQL在进行alter table等DDL操作时,有时会出现Waiting for tab ...

  7. mysql出现Waiting for table metadata lock的原因及解决方案

    最近经常遇到mysql数据库死锁,郁闷死, show processlist; 时 Waiting for table metadata lock 能一直锁很久 下面有官网的一段话,可以理解下 htt ...

  8. MySQL出现Waiting for table metadata lock的场景浅析

    MySQL版本为5.6.12. 在进行alter table操作时,有时会出现Waiting for table metadata lock的等待场景.而且,一旦alter table TableA的 ...

  9. 修改字段字符集 mysql 修改 锁表 show processlist; 查看进程 Waiting for table metadata lock

    ALTER TABLE `question` MODIFY COLUMN `title` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unico ...

随机推荐

  1. spring的一个小例子(二)--解析前面的小例子

    接上篇:http://www.cnblogs.com/xuejupo/p/5236448.html 首先应该明白,一个web项目,web.xml是入口. 然后下面来分析上篇博客中出现的web.xml: ...

  2. MySQL平时记录笔记

    零,mysql的安装 http://blog.csdn.net/mhmyqn/article/details/17043921 https://www.cnblogs.com/wangjunyan/p ...

  3. es6剩余参数

    function show(a,b,...args){ console.log(a) console.log(b) console.log(args) } show(10,20,30,30,36)

  4. PAT乙级1003

    1003 我要通过! (20 point(s)) “答案正确”是自动判题系统给出的最令人欢喜的回复.本题属于 PAT 的“答案正确”大派送 —— 只要读入的字符串满足下列条件,系统就输出“答案正确”, ...

  5. git 的学习使用记录

    git initls -ahgit add xxxgit commit -m "some message" git statusgit loggit log --pretty=on ...

  6. Python之旅Day8 socket网络编程

    socket网络编程 Socket是网络编程的一个抽象概念.通常我们用一个Socket表示“打开了一个网络链接”,而打开一个Socket需要知道目标计算机的IP地址和端口号,再指定协议类型即可.soc ...

  7. VSCode插件开发全攻略(一)概览

    文章索引 VSCode插件开发全攻略(一)概览 VSCode插件开发全攻略(二)HelloWord VSCode插件开发全攻略(三)package.json详解 VSCode插件开发全攻略(四)命令. ...

  8. Spark学习之wordcount程序

    实例代码: import org.apache.spark.SparkConf; import org.apache.spark.api.java.JavaPairRDD; import org.ap ...

  9. JSTL标签库的基本教程之核心标签库(二)

    JSTL标签库的基本教程之核心标签库(二) 核心标签库 标签 描述 <c:out> 用于在JSP中显示数据,就像<%= ... > <c:set> 用于保存数据 & ...

  10. Javascript高级编程学习笔记(74)—— 表单(2)表单提交及重置

    表单提交 表单的很大一部分作用就是帮助用户完成和服务器的交互 所以表单提交是表单中比较重要的部分 虽然现如今的大部分应用场景都使用 AJAX 的异步请求来代替表单,但是仍有部分操作需要使用表单来完成, ...