本文由云+社区发表python
2018年某个周末,接到连续数据库的告警,告警信息以下:mysql
看到too many connection的报错信息,基本上能够把问题定位在:sql
(1)机器负载飙升,致使SQL执行效率降低,致使链接推积数据库
(2)业务访问量突增(或者有SQL注入现象),致使链接数打满服务器
(3)出现“死锁”或者锁竞争严重,致使大量SQL堆积session
(1)机器的各项性能指标都显示正常, 没有出现高负载现象,暂时先排除了这种缘由并发
(2)查看监控信息,发如今链接数打满的时间点前并无访问量突增的趋势,同时经过检查告警信息并无发现有注入工单性能
(3)最后上到服务器上查看下SQL的执行状况3d
①查看show full processlist;code
大量的请求都是在“Waiting for table metadata lock”,能够分红三类请求:
②分析Waiting for table metadata lock
通常来讲常见的“Waiting for table metadata lock”会出如今DDL操做或者是有未提交的事务上,从information_schema.processlist表中,没有发现有DDL操做,而可以产生MDL锁的操做也只剩下rename,可是根据SQL执行的状态,rename操做也是在等待MDL锁,因此rename操做应该是被阻塞的操做,而不是产生MDL锁的操做。
接着咱们来查看下死锁和事务的相关指标:
如今基本又排除了显示的死锁问题,那是从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掉了这个进程,果真推积的请求瞬间就执行完成了,也之间印证了刚刚上述推论。
在与开发同窗沟经过程中,开发同窗说库中是myisam表因此不会主动开启事务,在代码里也没有设置autocommit=0的代码,那么根本缘由在哪?
当咱们定位到这台服务器上的请求都是来自python的定时脚本,使用python 操做mysql的时候,使用了其pymysql模块,可是在进行插入操做的时候,必须使用受到提交事务。Python的pymysql模块默认是会设置autocommit=0的。
让咱们来对比一下其余一样使用python访问的正常链接请求,再断开前都会手动的commit。
找到缘由后有思考了下,是否是能够在建连后就设置autocommit=1呢?这样对于以后新变动的SQL就不要再考虑到手动commit的事情了,能够经过在初始化链接池的时候,对每个链接进行设置,即
(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仍然会被堵住
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)
(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 送你一份技术课程大礼包!