MySQL线程处于Waiting for table flush的分析
最近遇到一个案例,很多查询被阻塞没有返回结果,使用show processlist查看,发现不少MySQL线程处于Waiting for table flush状态,查询语句一直被阻塞,只能通过Kill进程来解决。那么我们先来看看Waiting for table flush的官方解释:https://dev.mysql.com/doc/refman/5.6/en/general-thread-states.html
Waiting for table flush
The thread is executing FLUSH TABLES and is waiting for all threads to close their tables, or the thread got a notification that the underlying structure for a table has changed and it needs to reopen the table to get the new structure. However, to reopen the table, it must wait until all other threads have closed the table in question.
This notification takes place if another thread has used FLUSH TABLES or one of the following statements on the table in question: FLUSH TABLES tbl_name, ALTER TABLE, RENAME TABLE, REPAIR TABLE, ANALYZE TABLE, or OPTIMIZE TABLE.
那么我们接下来模拟一下线程处于Waiting for table flush状态的情况,如所示:
在第一个会话连接(connection id=13)中,我们使用lock table 锁定表test。
mysql> use MyDB;
Database changed
mysql> select connection_id();
+-----------------+
| connection_id() |
+-----------------+
| 13 |
+-----------------+
1 row in set (0.00 sec)
mysql> lock table test read;
Query OK, 0 rows affected (0.00 sec)
mysql>
在第二个会话连接(connection id=17)中,我们执行flush table 或 flush table test 皆可。此时你会发现flush table处于阻塞状态。
mysql> use MyDB;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
mysql> select connection_id();
+-----------------+
| connection_id() |
+-----------------+
| 17 |
+-----------------+
1 row in set (0.00 sec)
mysql> flush table test;

在第三个会话/连接中,当你切换到MyDB时,就会提示“You can turn off this feature to get a quicker startup with -A” ,此时处于阻塞状态。此时你退出会话,使用参数-A登录数据库后,你如果查询test表,就会处于阻塞状态(当然查询其它表不会被阻塞)。如下所示:
mysql> use MyDB;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
mysql> use MyDB;
Database changed
mysql> select * from test;

在第四个会话/连接,我们用show processlist查看到当前数据库所有连接线程状态,你会看到17、18都处于Waiting for table flush的状态。如下截图所示:
mysql> show processlist;
+----+------+-----------+------+---------+------+-------------------------+--------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----+------+-----------+------+---------+------+-------------------------+--------------------+
| 13 | root | localhost | MyDB | Sleep | 90 | | NULL |
| 14 | root | localhost | NULL | Query | 0 | init | show processlist |
| 17 | root | localhost | MyDB | Query | 52 | Waiting for table flush | flush table test |
| 18 | root | localhost | MyDB | Query | 9 | Waiting for table flush | select * from test |
+----+------+-----------+------+---------+------+-------------------------+--------------------+
4 rows in set (0.00 sec)
mysql>

mysql> show processlist;
+----+------+-----------+------+---------+------+-------------------------+--------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----+------+-----------+------+---------+------+-------------------------+--------------------+
| 13 | root | localhost | MyDB | Sleep | 90 | | NULL |
| 14 | root | localhost | NULL | Query | 0 | init | show processlist |
| 17 | root | localhost | MyDB | Query | 52 | Waiting for table flush | flush table test |
| 18 | root | localhost | MyDB | Query | 9 | Waiting for table flush | select * from test |
+----+------+-----------+------+---------+------+-------------------------+--------------------+
4 rows in set (0.00 sec)
mysql>
mysql>
mysql>
mysql>
mysql> show open tables where in_use >=1;
+----------+-------+--------+-------------+
| Database | Table | In_use | Name_locked |
+----------+-------+--------+-------------+
| MyDB | test | 1 | 0 |
+----------+-------+--------+-------------+
1 row in set (0.00 sec)
mysql> kill 17;
Query OK, 0 rows affected (0.00 sec)
mysql> show processlist;
+----+------+-----------+------+---------+------+-------------------------+--------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----+------+-----------+------+---------+------+-------------------------+--------------------+
| 13 | root | localhost | MyDB | Sleep | 442 | | NULL |
| 14 | root | localhost | NULL | Query | 0 | init | show processlist |
| 18 | root | localhost | MyDB | Query | 361 | Waiting for table flush | select * from test |
+----+------+-----------+------+---------+------+-------------------------+--------------------+
3 rows in set (0.00 sec)
mysql> kill 13;
Query OK, 0 rows affected (0.00 sec)
mysql> show processlist;
+----+------+-----------+------+---------+------+-------+------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----+------+-----------+------+---------+------+-------+------------------+
| 14 | root | localhost | NULL | Query | 0 | init | show processlist |
| 18 | root | localhost | MyDB | Sleep | 427 | | NULL |
+----+------+-----------+------+---------+------+-------+------------------+
2 rows in set (0.00 sec)
mysql>
| 
注意:我们需要Kill线程13, Kill掉线程17是解决不了问题的。
生产环境中,很多时候可能不是lock table read引起的阻塞,而是由于慢查询,导致flush table一直无法关闭该表而一直处于等待状态,例如下面测试案例中,我使用同一张大表做笛卡尔积模拟一个慢查询,其它操作相同,如下所示,你会看到同样产生了Waiting for table flush
mysql> SELECT T.* FROM TEST1 T, TEST1 L;

另外,网上有个案例,mysqldump备份时,如果没有使用参数—single-transaction 或由于同时使用了flush-logs与—single-transaction两个参数也可能引起这样的等待场景,这个两个参数放在一起,会在开始dump数据之前先执行一个FLUSH TABLES操作。
解决方案:
出现Waiting for table flush时,我们一般需要找到那些表被lock住或那些慢查询导致flush table一直在等待而无法关闭该表。然后Kill掉对应的线程即可,但是如何精准定位是一个挑战,尤其是生产环境,你使用show processlist会看到大量的线程。让你眼花缭乱的,怎么一下子定位问题呢?
对于慢查询引起的其它线程处于Waiting for table flush状态的情形:
可以查看show processlist中Time值很大的线程。然后甄别确认后Kill掉,如上截图所示,会话连接14就是引起阻塞的源头SQL。有种规律就是这个线程的Time列值必定比被阻塞的线程要高。这个就能过滤很多记录。
对于lock table read引起的其它线程处于Waiting for table flush状态的情形:
对于实验中使用lock table read这种情况,这种会话可能处于Sleep状态,而且它也不会出现在show engine innodb status \G命令的输出信息中。 即使show open tables where in_use >=1;能找到是那张表被lock住了,但是无法定位到具体的线程(连接),其实这个是一个头痛的问题。但是inntop这款利器就可以定位到,如下所示,线程17锁住了表test,在innotop里面就能定位到是线程17。所谓工欲善其事必先利其器!


另外,在官方文档中ALTER TABLE, RENAME TABLE, REPAIR TABLE, ANALYZE TABLE, or OPTIMIZE TABLE都能引起这类等待,下面也做了一些简单测试,如下所示:
Waiting for table flush的另外一个场景
会话连接(connection id=18)执行下面SQL语句,模拟一个慢查询SQL
mysql> select connection_id();
+-----------------+
| connection_id() |
+-----------------+
| 18 |
+-----------------+
1 row in set (0.00 sec)
mysql> select name, sleep(64) from test;
会话连接(connection id=6)执行下面SQL语句,分析表test
mysql> select connection_id();
+-----------------+
| connection_id() |
+-----------------+
| 6 |
+-----------------+
1 row in set (0.00 sec)
mysql> analyze table test;
+-----------+---------+----------+----------+
| Table | Op | Msg_type | Msg_text |
+-----------+---------+----------+----------+
| MyDB.test | analyze | status | OK |
+-----------+---------+----------+----------+
1 row in set (0.04 sec)
mysql>
会话连接(connection id=8)执行下面SQL语句
mysql> select connection_id();
+-----------------+
| connection_id() |
+-----------------+
| 8 |
+-----------------+
1 row in set (0.00 sec)
mysql> select * from test;
查看线程的状态,你会发现被阻塞的会话处于 Waiting for table flush状态。 因为当对表做了ANALYZE TABLE后,后台针对该表的查询需要等待,因为MySQL已经检测到该表内部变化,需要使用FLUSH TABLE关闭然后重新打开该表,所以当你查询该表时,就会处于 Waiting for table flush
mysql> show processlist;
+----+------+-----------+------+---------+------+-------------------------+----------------------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----+------+-----------+------+---------+------+-------------------------+----------------------------------+
| 6 | root | localhost | MyDB | Sleep | 22 | | NULL |
| 8 | root | localhost | MyDB | Query | 14 | Waiting for table flush | select * from test |
| 15 | root | localhost | NULL | Sleep | 3 | | NULL |
| 16 | root | localhost | NULL | Query | 0 | init | show processlist |
| 18 | root | localhost | MyDB | Query | 46 | User sleep | select name, sleep(64) from test |
+----+------+-----------+------+---------+------+-------------------------+----------------------------------+
5 rows in set (0.00 sec)
mysql>

Waiting for table metadata lock
会话连接(connection id=17)执行下面SQL语句,模拟一个慢查询SQL
mysql> select connection_id();
+-----------------+
| connection_id() |
+-----------------+
| 17 |
+-----------------+
1 row in set (0.00 sec)
mysql> select name, sleep(100) from test;
会话连接(connection id=6)执行下面SQL语句, 修改表结构操作
mysql> select connection_id();
+-----------------+
| connection_id() |
+-----------------+
| 6 |
+-----------------+
1 row in set (0.00 sec)
mysql> alter table test add tname varchar(10); // rename table test to kkk 同样会引起Waiting for table metadata lock
会话连接(connection id=8)执行下面SQL语句,查询表test
mysql> select connection_id();
+-----------------+
| connection_id() |
+-----------------+
| 8 |
+-----------------+
1 row in set (0.00 sec)
mysql> select * from test;
查看线程的状态,你会发现被阻塞的会话处于 Waiting for table metadata lock状态。
mysql> show processlist;
+----+------+-----------+------+---------+------+---------------------------------+----------------------------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----+------+-----------+------+---------+------+---------------------------------+----------------------------------------+
| 6 | root | localhost | MyDB | Query | 19 | Waiting for table metadata lock | alter table test add tname varchar(10) |
| 8 | root | localhost | MyDB | Query | 6 | Waiting for table metadata lock | select * from test |
| 15 | root | localhost | NULL | Sleep | 8 | | NULL |
| 16 | root | localhost | NULL | Query | 0 | init | show processlist |
| 17 | root | localhost | MyDB | Query | 55 | User sleep | select name, sleep(100) from test |
+----+------+-----------+------+---------+------+---------------------------------+----------------------------------------+
5 rows in set (0.00 sec)
mysql>

参考资料:
https://www.percona.com/blog/2013/02/27/mysql-optimizer-analyze-table-and-waiting-for-table-flush/
http://www.cnblogs.com/jackhub/p/3841004.html
http://myrock.github.io/2014/11/20/mysql-waiting-for-table-flush/
http://mysql.taobao.org/monthly/2016/03/10/
MySQL线程处于Waiting for table flush的分析的更多相关文章
- MySQL进程处于Waiting for table flush的分析
		
最近遇到一个案例,很多查询被阻塞没有返回结果,使用show processlist查看,发现不少MySQL线程处于Waiting for table flush状态,查询语句一直被阻塞,只能通过K ...
 - mysql Waiting for table flush
		
应用突然被hang住了,tomcat日志报错,所有涉及到数据库的操作都报错,卡死. show processlist 查看到大量的:Waiting for table flush 应该是 进行了 dd ...
 - Waiting for table flush 阻塞查询的问题
		
1.此状态表示大量thread正在等待慢查询语句执行完成. 原因: The thread got a notification that the underlying structure for a ...
 - mysql错误: waiting for table metadata lock
		
今天突然发现truncate一个表都慢到不行,于是 SHOW PROCESSLIST 发现错误:waiting for table metadata lock解决方法:查看information_sc ...
 - MySQL - 问题集 - "Waiting for table metadata lock"(待完善)
		
待完善.show processlist; 可参考1:http://blog.csdn.net/huochuangchuang/article/details/49423893 可参考2:http:/ ...
 - 记一次MySQL出现Waiting for table metadata lock的原因、排查过程与解决方法
		
任务背景:将sql文件通过shell直接导入到mysql中执行(还原) bug表现:导入后java项目卡死 过程: 1.网上乱搜一通,无意间看到一篇文章,这篇文章说明了如何开启mysql的genera ...
 - MySQL线程状态详解
		
前言: 我们常用 show processlist 或 show full processlist 查看数据库连接状态,其中比较关注的是 State 列,此列表示该连接此刻所在的状态.那么你真的了解不 ...
 - 转 mysql 备份导致 waiting for global read lock
		
######转 https://blog.csdn.net/weixin_34038652/article/details/92129498 近业务高峰期间经常会有开发跳起来说应用连接数据库超时了! ...
 - MySQL大量线程处于Opening tables的问题分析
		
[作者] 王栋:携程技术保障中心数据库专家,对数据库疑难问题的排查和数据库自动化智能化运维工具的开发有强烈的兴趣. [问题描述] 最近有一台MySQL5.6.21的服务器,在应用发布后,并发线程Thr ...
 
随机推荐
- Apache XBean相关说明,待补充
			
前言 最近在看ActiveMQ5.15.0源码,发现ActiveMQ实际上是基于spring实现的,其配置文件activemq.xml中有个broker元素节点,使用的就是Apache XBean的配 ...
 - 【杂谈】FilterChain相关知识整理
			
前言 做后台的,Filter肯定没少配置,但是知晓其原理的可能不多.在这之前我也不懂,但这并不影响业务开发,同时也有其他的知识要学,所以一直就没看.这阵子有点闲,刚好在看<How Tomcat ...
 - 机器学习基础:(Python)训练集测试集分割与交叉验证
			
在上一篇关于Python中的线性回归的文章之后,我想再写一篇关于训练测试分割和交叉验证的文章.在数据科学和数据分析领域中,这两个概念经常被用作防止或最小化过度拟合的工具.我会解释当使用统计模型时,通常 ...
 - 【Bootstrap系列】详解Bootstrap-table
			
本篇文章将与大家分享bootstrap-table插件,借助于它实现基本的增删改查,导入导出,分页,父子表等. 至于其他技术,如冻结表头,列排列,行拖动,列拖动等,会在后续文章中与大家分享. 一 ...
 - ZooKeeper系列(4):ZooKeeper的配置文件详解
			
ZooKeeper系列文章:https://www.cnblogs.com/f-ck-need-u/p/7576137.html#zk zkServer.sh读取的默认配置文件是$ZOOKEEPER_ ...
 - 聊聊数据库~2.SQL环境篇
			
传统数据库 上篇文章:聊聊数据库~开篇 https://www.cnblogs.com/dotnetcrazy/p/9690466.html 本来准备直接开讲NoSQL的(当时开篇就是说的NoSQL) ...
 - 怎么将XML字符串转换为XmlDocument,并获取部分节点值
			
我们的目标:解析XML字符串,并获取节点:z:row下的属性:ows_LinkFilename的值集合??? XML字符串数据结构如下: <listitems xmlns:s='uuid:BDC ...
 - PHP 依赖注入和控制反转再谈(二)
			
今天有个朋友看到yii2中介绍的依赖注入一头雾水,之前我写过类似的文章发给他看了,可能还没深入理解吧,这里我再通俗点描述下依赖注入的原理吧,尽可能滴说通俗易懂一点吧:先还是扯下概念性滴问题(概念问题我 ...
 - nginx配置反向代理和负载均衡
			
一.反向代理 说明:应该有一个nginx服务器有多个应用服务器(可以是tomcat),本文使用一台虚拟机,安装一个nginx,多个tomcat,来模拟 upstream tomcats{ server ...
 - 向Spring容器中注册组件的方法汇总小结
			
1.通过xml定义 <bean class=""> <property name="" value=""></ ...