MySQL版本为5.6.12。
在进行alter table操作时,有时会出现Waiting for table metadata lock的等待场景。而且,一旦alter table TableA的操作停滞在Waiting for table metadata lock的状态,后续对TableA的任何操作(包括读)都无法进行,也会在Opening tables的阶段进入Waiting for table metadata lock的队列。如果是产品环境的核心表出现了这样的锁等待队列,就会造成灾难性的后果。
造成alter table产生Waiting for table metadata lock的原因其实很简单,一般是以下几个简单的场景:
场景一:
通过show processlist可以看到TableA上有正在进行的操作(包括读),此时alter table语句无法获取到metadata 独占锁,会进行等待。
这是最基本的一种情形,这个和mysql 5.6中的online ddl并不冲突。一般alter table的操作过程中(见下图),在after create步骤会获取metadata 独占锁,当进行到altering table的过程时(通常是最花时间的步骤),对该表的读写都可以正常进行,这就是online ddl的表现,并不会像之前在整个alter table过程中阻塞写入。(当然,也并不是所有类型的alter操作都能online的,具体可以参见官方手册:http://dev.mysql.com/doc/refman/5.6/en/innodb-create-index-overview.html)
场景二:
通过show processlist看不到TableA上有任何操作,但实际上存在有未提交的事务,可以在information_schema.innodb_trx中查看到。在事务没有完成之前,TableA上的锁不会释放,alter table同样获取不到metadata的独占锁。
场景三:
通过show processlist看不到TableA上有任何操作,在information_schema.innodb_trx中也没有任何进行中的事务。这很可能是因为在一个显式的事务中,对TableA进行了一个失败的操作(比如查询了一个不存在的字段),这时事务没有开始,但是失败语句获取到的锁依然有效。从performance_schema.events_statements_current表中可以查到失败的语句。
官方手册上对此的说明如下:
If the server acquires metadata locks for a statement that is syntactically valid but fails during execution, it does not release the locks early. Lock release is still deferred to the end of the transaction because the failed statement is written to the binary log and the locks protect log consistency.
也就是说除了语法错误,其他错误语句获取到的锁在这个事务提交或回滚之前,仍然不会释放掉。because the failed statement is written to the binary log and the locks protect log consistency 但是解释这一行为的原因很难理解,因为错误的语句根本不会被记录到二进制日志。
总之,alter table的语句是很危险的,在操作之前最好确认对要操作的表没有任何进行中的操作、没有未提交事务、也没有显式事务中的报错语句。如果有alter table的维护任务,在无人监管的时候运行,最好通过lock_wait_timeout设置好超时时间,避免长时间的metedata锁等待。
版权声明:本文为博主原创文章,未经博主允许不得转载。
最近项目中的数据库查询经常挂起,应用程序启动后也报操作超时。测试人员就说数据库又挂了(貌似他们眼中的连接失败,查询无果都是挂了),通过 show processlist
一看,满屏都是 Waiting for table metadata lock
状态的连接。第一反应就是kill掉这些连接,奈何连接实在太多,实在kill不过来,于是重启服务,貌似重启果真能解决90%的问题,但如果不找到问题原因,问题也肯定会再次出现。
在网上查询得知MySQL在进行一些alter table等DDL操作时,如果该表上有未提交的事务则会出现 Waiting for table metadata lock
,而一旦出现metadata lock,该表上的后续操作都会被阻塞(详见 http://www.bubuko.com/infodetail-1151112.html)。所以这个问题需从两方面解决:
1. 查看未提交事务
从 information_schema.innodb_trx 表中查看当前未提交的事务
select trx_state, trx_started, trx_mysql_thread_id, trx_query from information_schema.innodb_trxG
- 1
(G作为结束符时,MySQL Client会把结果以列模式展示,对于列比较长的表,展示更直观)
字段意义:
- trx_state: 事务状态,一般为RUNNING
- trx_started: 事务执行的起始时间,若时间较长,则要分析该事务是否合理
- trx_mysql_thread_id: MySQL的线程ID,用于kill
- trx_query: 事务中的sql
一般只要kill掉这些线程,DDL操作就不会Waiting for table metadata lock。
2. 调整锁超时阈值
lock_wait_timeout
表示获取metadata lock的超时(单位为秒),允许的值范围为1到31536000(1年)。 默认值为31536000。详见 https://dev.mysql.com/doc/refman/5.6/en/server-system-variables.html#sysvar_lock_wait_timeout 。默认值为一年!!!已哭瞎!将其调整为30分钟
set session lock_wait_timeout = 1800;
set global lock_wait_timeout = 1800;
- 1
- 2
好让出现该问题时快速故障(failfast)
版权声明:本文为博主原创文章,未经博主允许不得转载。
那么会有同学问,为什么在Mysql 5.5.3之前就很少遇到这种锁呢?原因是
5.5.3版本之前,MySQL事务对于表结构元数据(Metadata)的锁定是语句(statement)粒度的:即语句执行完成后,不管事务是否可以完成,其表结构就可以被其他会话更新掉!
引入Metadata lock后,表结构元数据(Metadata)的锁定变成了事务(transaction)粒度的,即只有事务结束时才会释放Metadata lock。
怎么出现的?
程序或者脚本显式开启事务(start transaction),该事务内的query语句(包含select)会占用相关表的metadata lock(profile:Opening tables阶段)。导致后续的所有DDL操作语句全部被阻塞,原因就是获取不到metadata lock。(在mysql 5.6版本后有优化)官方手册参阅:http://dev.mysql.com/doc/refman/5.6/en/innodb-create-index-overview.html
我们下面以现网case来探讨这个问题的出现于解决:
case
业务执行一条简单的alter table 操作,增加一个字段,很普通的一条sql,而且表不大,数据量很少,执行却消耗几百秒没反应(现场忘记截图)
补充一个测试图
从图可以看出业务执行的语句遇到metadata lock了。
1、 分析mysql的实例的情况
1.1 mysql> show processlist;
除了有一个 Waiting for table 之外没有其它的操作进程,全部是sleep进程。这时你觉得奇怪吗?为什么没有其它的进程锁住这个表,会导致这个ddl语句一直卡住呢? 我们接着分析。
1.2 查看表是否太大 mysql> show table status like 'tbl_xx' G
图1.2
看出表非常小,不存在由于数据量大导致更新慢的问题;
1.3 查看引擎状态 mysql> show engine innodb status G
数据量太大,一屏幕都显示不完,不看了。
既然几个比较直接的方法都查不到原因,那只能更深入的查下了,我打算从数据字典中查下(information_schema,performance_schema):
1.4,查找当前等待事务:
mysql> select * from performance_schema .events_waits_current;
Empty set (0.03 sec)
显示空。
查找information_schema中的事件表(EVENTS)、锁等待表(INNODB_LOCK_WAITS),innodb当前出现的锁(INNODB_LOCKS)均没看到异常(这里就不贴图了)。
1.5 查找事务
既然造成该锁的原因是事务没有提交导致的,那我们应该去查找当前是否有事务在运行(runing注:由于事务一直是runing状态,这也就是为什么我之前查找各种锁都找不到的原因)
mysql> select * from information_schema.innodb_trx;
(此图又被刷不见了)不过有重大发现:一个trx_mysql_thread_id: 275255348 是从trx_started: 2015-12-03 14:58:45 一直处于runing状态的。
既然我们找到了id了 那我们再回顾使用show processlist查找该ID就行了:
发现了吗,该ID一直是sleep状态。很难发现该进程打开了这个表(可以通过show open tables 查看当前打开的表)。
解决办法:询问了开发这个点的脚本,操作。确认后通过后台mysql 直接kill掉这个进程,业务的alter操作瞬间完成。
附:欢迎大家一起探讨研究
分类: MySQL
环境说明:
1.问题描述
2.为什么需要Metadata lock
3.Metadata lock监控
4.导致Metadata Lock的场景
场景1:
场景2:
场景3:
5.如何快速处理Metadata lock
6.如何避免Metadata Lock
6.1.关注autocommit
- 数据库autocommit
- 客户端工具的autocommit