请进行以下操作实现复现: 


表结构如下:

mysql> create table xcy_test_utf8( id  int ,

    -> name varchar(2),

    -> addr varchar(200));

会话A:

mysql> begin;

Query OK, 0 rows affected (0.00 sec)


mysql> insert into xcy_test_utf8 values(1,'xcy','beijing');

ERROR 1406 (22001): Data too long for column 'name' at row 1


发现name字段太短, 然后通知DBA或者自己进行修改。

会话B:

alter table  xcy_test_utf8  modify name varchar(30);

没有马上执行完成, 还以为是表太大,等等。。。。。。


已经过了一会儿了, 发现还没有执行完,是不是不对劲?通过其他会话执行show processlist ,发现了 Waiting for table metadata lock.


为什么会有 Waiting for table metadata lock 的现象?原因很简单, 因为有事物没有提交。嗯,确实如此,因为刚才执行了insert语句, 因为name列定义的长度太短,然后报错了。  目前还没有发起回滚命令 。 


但是,作为后台的数据库管理人员,怎么去发现有未提交的事务?作者知道的手法如下:(如果有朋友知道更多方法,请留言)


方法1:show engine innodb status \G


发现一个关于MySQL的metadata lock的BUG_java

没有发现状态为active(未提交) 的事务


方法二:查询information_schema.innodb_trx 表


没有发现有事务。


两个方式都没有发现活跃事务,但alter 语句却被活活堵塞了。文章快写完了,alter语句还在执行呢。 自然也会堵塞其他对这个表进行更新操作的会话,形成一个堵塞队列。


如果是默认的lock_wait_timeout , alter 语句将在31536000 秒后锁超时。


所以,如果发现您的alter 语句被莫名其妙的堵塞了,通过上面的方法还没有发现是被谁堵塞了,就别慌了,也不用怀疑您自己的技能问题。

 

当前处理方法:

  1. 尽快取消或者kill执行alter语句的会话,以免造成堵塞队列,影响系统的可用性。

  2. 如果alter语句必须现在就执行,通知应用管理人员后,通过show processlist 命令,找出所有状态为Sleep的会话,然后全部杀掉。(一个一个杀也行,直到alter语句可以执行为止,但如果手工做,速度慢.)

  

    3.  另外,建议在执行alter语句之前,修改会话级lock_wait_timeout参数,例如设置10秒钟,10秒钟还没有获得metadata 锁,将自动锁超时, 以免长时间堵塞对这个表的dml操作。


     4.对于研发人员,执行sql失败时,请一定尽快执行rollback命令。


猜测bug出现的原因:

        在执行SQL失败后,没有清理该SQL所获取的metadata 锁。 但如果修补的话,需要考虑mysql目前是半回滚机制,只回滚当前出错的sql,而不是整个事务(整个事务回滚需要执行rollback命令),所以也不能在sql执行失败时,就直接清理掉metadata 锁,需要有个判断机制。  


         因为在5.7.18上能够100%的复现,后续将向官方提bug.