您的位置:首页 > 数据库 > MySQL

MySQL线程处于Waiting for table flush的分析

2017-08-18 17:22 183 查看
转自http://www.cnblogs.com/kerrycode/p/7388968.html

最近遇到一个案例,很多查询被阻塞没有返回结果,使用showprocesslist查看,发现不少MySQL线程处于Waitingfortableflush状态,查询语句一直被阻塞,只能通过Kill进程来解决。那么我们先来看看Waitingfortableflush的官方解释:https://dev.mysql.com/doc/refman/5.6/en/general-thread-states.html

Waitingfortableflush

ThethreadisexecutingFLUSHTABLESandiswaitingforallthreadstoclosetheirtables,orthethreadgotanotificationthattheunderlyingstructureforatablehaschangedanditneedstoreopenthetabletogetthenewstructure.However,toreopenthetable,itmustwaituntilallotherthreadshaveclosedthetableinquestion.

ThisnotificationtakesplaceifanotherthreadhasusedFLUSHTABLESoroneofthefollowingstatementsonthetableinquestion:FLUSHTABLEStbl_name,ALTERTABLE,RENAMETABLE,REPAIRTABLE,ANALYZETABLE,orOPTIMIZETABLE.

那么我们接下来模拟一下线程处于Waitingfortableflush状态的情况,如所示:

在第一个会话连接(connectionid=13)中,我们使用locktable锁定表test。

mysql>useMyDB;


Databasechanged


mysql>selectconnection_id();


+-----------------+


|connection_id()|


+-----------------+


|13|


+-----------------+


1rowinset(0.00sec)




mysql>locktabletestread;


QueryOK,0rowsaffected(0.00sec)




mysql>


在第二个会话连接(connectionid=17)中,我们执行flushtable或flushtabletest皆可。此时你会发现flushtable处于阻塞状态。

mysql>useMyDB;


Readingtableinformationforcompletionoftableandcolumnnames


Youcanturnoffthisfeaturetogetaquickerstartupwith-A




Databasechanged


mysql>selectconnection_id();


+-----------------+


|connection_id()|


+-----------------+


|17|


+-----------------+


1rowinset(0.00sec)




mysql>flushtabletest;






在第三个会话/连接中,当你切换到MyDB时,就会提示“Youcanturnoffthisfeaturetogetaquickerstartupwith-A”,此时处于阻塞状态。此时你退出会话,使用参数-A登录数据库后,你如果查询test表,就会处于阻塞状态(当然查询其它表不会被阻塞)。如下所示:

mysql>useMyDB;

Readingtableinformationforcompletionoftableandcolumnnames

Youcanturnoffthisfeaturetogetaquickerstartupwith-A

mysql>useMyDB;

Databasechanged

mysql>select*fromtest;





在第四个会话/连接,我们用showprocesslist查看到当前数据库所有连接线程状态,你会看到17、18都处于Waitingfortableflush的状态。如下截图所示:

mysql>showprocesslist;


+----+------+-----------+------+---------+------+-------------------------+--------------------+


|Id|User|Host|db|Command|Time|State|Info|


+----+------+-----------+------+---------+------+-------------------------+--------------------+


|13|root|localhost|MyDB|Sleep|90||NULL|


|14|root|localhost|NULL|Query|0|init|showprocesslist|


|17|root|localhost|MyDB|Query|52|Waitingfortableflush|flushtabletest|


|18|root|localhost|MyDB|Query|9|Waitingfortableflush|select*fromtest|


+----+------+-----------+------+---------+------+-------------------------+--------------------+


4rowsinset(0.00sec)




mysql>






mysql>showprocesslist;


+----+------+-----------+------+---------+------+-------------------------+--------------------+


|Id|User|Host|db|Command|Time|State|Info|


+----+------+-----------+------+---------+------+-------------------------+--------------------+


|13|root|localhost|MyDB|Sleep|90||NULL|


|14|root|localhost|NULL|Query|0|init|showprocesslist|


|17|root|localhost|MyDB|Query|52|Waitingfortableflush|flushtabletest|


|18|root|localhost|MyDB|Query|9|Waitingfortableflush|select*fromtest|


+----+------+-----------+------+---------+------+-------------------------+--------------------+


4rowsinset(0.00sec)




mysql>


mysql>


mysql>


mysql>


mysql>showopentableswherein_use>=1;


+----------+-------+--------+-------------+


|Database|Table|In_use|Name_locked|


+----------+-------+--------+-------------+


|MyDB|test|1|0|


+----------+-------+--------+-------------+


1rowinset(0.00sec)




mysql>kill17;


QueryOK,0rowsaffected(0.00sec)




mysql>showprocesslist;


+----+------+-----------+------+---------+------+-------------------------+--------------------+


|Id|User|Host|db|Command|Time|State|Info|


+----+------+-----------+------+---------+------+-------------------------+--------------------+


|13|root|localhost|MyDB|Sleep|442||NULL|


|14|root|localhost|NULL|Query|0|init|showprocesslist|


|18|root|localhost|MyDB|Query|361|Waitingfortableflush|select*fromtest|


+----+------+-----------+------+---------+------+-------------------------+--------------------+


3rowsinset(0.00sec)




mysql>kill13;


QueryOK,0rowsaffected(0.00sec)




mysql>showprocesslist;


+----+------+-----------+------+---------+------+-------+------------------+


|Id|User|Host|db|Command|Time|State|Info|


+----+------+-----------+------+---------+------+-------+------------------+


|14|root|localhost|NULL|Query|0|init|showprocesslist|


|18|root|localhost|MyDB|Sleep|427||NULL|


+----+------+-----------+------+---------+------+-------+------------------+


2rowsinset(0.00sec)




mysql>


|



注意:我们需要Kill线程13,Kill掉线程17是解决不了问题的。

生产环境中,很多时候可能不是locktableread引起的阻塞,而是由于慢查询,导致flushtable一直无法关闭该表而一直处于等待状态,例如下面测试案例中,我使用同一张大表做笛卡尔积模拟一个慢查询,其它操作相同,如下所示,你会看到同样产生了Waitingfortableflush

mysql>SELECTT.*FROMTEST1T,TEST1L;





另外,网上有个案例,mysqldump备份时,如果没有使用参数—single-transaction或由于同时使用了flush-logs与—single-transaction两个参数也可能引起这样的等待场景,这个两个参数放在一起,会在开始dump数据之前先执行一个FLUSHTABLES操作。

解决方案:

出现Waitingfortableflush时,我们一般需要找到那些表被lock住或那些慢查询导致flushtable一直在等待而无法关闭该表。然后Kill掉对应的线程即可,但是如何精准定位是一个挑战,尤其是生产环境,你使用showprocesslist会看到大量的线程。让你眼花缭乱的,怎么一下子定位问题呢?

对于慢查询引起的其它线程处于Waitingfortableflush状态的情形:

可以查看showprocesslist中Time值很大的线程。然后甄别确认后Kill掉,如上截图所示,会话连接14就是引起阻塞的源头SQL。有种规律就是这个线程的Time列值必定比被阻塞的线程要高。这个就能过滤很多记录。

对于locktableread引起的其它线程处于Waitingfortableflush状态的情形:

对于实验中使用locktableread这种情况,这种会话可能处于Sleep状态,而且它也不会出现在showengineinnodbstatus\G命令的输出信息中。即使showopentableswherein_use>=1;能找到是那张表被lock住了,但是无法定位到具体的线程(连接),其实这个是一个头痛的问题。但是inntop这款利器就可以定位到,如下所示,线程17锁住了表test,在innotop里面就能定位到是线程17。所谓工欲善其事必先利其器!









另外,在官方文档中ALTERTABLE,RENAMETABLE,REPAIRTABLE,ANALYZETABLE,orOPTIMIZETABLE都能引起这类等待,下面也做了一些简单测试,如下所示:

Waitingfortableflush的另外一个场景

会话连接(connectionid=18)执行下面SQL语句,模拟一个慢查询SQL

mysql>selectconnection_id();


+-----------------+


|connection_id()|


+-----------------+


|18|


+-----------------+


1rowinset(0.00sec)




mysql>selectname,sleep(64)fromtest;


会话连接(connectionid=6)执行下面SQL语句,分析表test

mysql>selectconnection_id();


+-----------------+


|connection_id()|


+-----------------+


|6|


+-----------------+


1rowinset(0.00sec)


mysql>analyzetabletest;


+-----------+---------+----------+----------+


|Table|Op|Msg_type|Msg_text|


+-----------+---------+----------+----------+


|MyDB.test|analyze|status|OK|


+-----------+---------+----------+----------+


1rowinset(0.04sec)




mysql>


会话连接(connectionid=8)执行下面SQL语句

mysql>selectconnection_id();


+-----------------+


|connection_id()|


+-----------------+


|8|


+-----------------+


1rowinset(0.00sec)




mysql>select*fromtest;


查看线程的状态,你会发现被阻塞的会话处于Waitingfortableflush状态。因为当对表做了ANALYZETABLE后,后台针对该表的查询需要等待,因为MySQL已经检测到该表内部变化,需要使用FLUSHTABLE关闭然后重新打开该表,所以当你查询该表时,就会处于Waitingfortableflush

mysql>showprocesslist;


+----+------+-----------+------+---------+------+-------------------------+----------------------------------+


|Id|User|Host|db|Command|Time|State|Info|


+----+------+-----------+------+---------+------+-------------------------+----------------------------------+


|6|root|localhost|MyDB|Sleep|22||NULL|


|8|root|localhost|MyDB|Query|14|Waitingfortableflush|select*fromtest|


|15|root|localhost|NULL|Sleep|3||NULL|


|16|root|localhost|NULL|Query|0|init|showprocesslist|


|18|root|localhost|MyDB|Query|46|Usersleep|selectname,sleep(64)fromtest|


+----+------+-----------+------+---------+------+-------------------------+----------------------------------+


5rowsinset(0.00sec)




mysql>






Waitingfortablemetadatalock

会话连接(connectionid=17)执行下面SQL语句,模拟一个慢查询SQL

mysql>selectconnection_id();


+-----------------+


|connection_id()|


+-----------------+


|17|


+-----------------+


1rowinset(0.00sec)




mysql>selectname,sleep(100)fromtest;


会话连接(connectionid=6)执行下面SQL语句,修改表结构操作

mysql>selectconnection_id();


+-----------------+


|connection_id()|


+-----------------+


|6|


+-----------------+


1rowinset(0.00sec)




mysql>altertabletestaddtnamevarchar(10);//renametabletesttokkk同样会引起Waitingfortablemetadatalock


会话连接(connectionid=8)执行下面SQL语句,查询表test

mysql>selectconnection_id();


+-----------------+


|connection_id()|


+-----------------+


|8|


+-----------------+


1rowinset(0.00sec)




mysql>select*fromtest;


查看线程的状态,你会发现被阻塞的会话处于Waitingfortablemetadatalock状态。

mysql>showprocesslist;


+----+------+-----------+------+---------+------+---------------------------------+----------------------------------------+


|Id|User|Host|db|Command|Time|State|Info|


+----+------+-----------+------+---------+------+---------------------------------+----------------------------------------+


|6|root|localhost|MyDB|Query|19|Waitingfortablemetadatalock|altertabletestaddtnamevarchar(10)|


|8|root|localhost|MyDB|Query|6|Waitingfortablemetadatalock|select*fromtest|


|15|root|localhost|NULL|Sleep|8||NULL|


|16|root|localhost|NULL|Query|0|init|showprocesslist|


|17|root|localhost|MyDB|Query|55|Usersleep|selectname,sleep(100)fromtest|


+----+------+-----------+------+---------+------+---------------------------------+----------------------------------------+


5rowsinset(0.00sec)




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/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: