请进行以下操作实现复现:
表结构如下:
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
没有发现状态为active(未提交) 的事务。
方法二:查询information_schema.innodb_trx 表
没有发现有事务。
两个方式都没有发现活跃事务,但alter 语句却被活活堵塞了。文章快写完了,alter语句还在执行呢。 自然也会堵塞其他对这个表进行更新操作的会话,形成一个堵塞队列。
如果是默认的lock_wait_timeout , alter 语句将在31536000 秒后锁超时。
所以,如果发现您的alter 语句被莫名其妙的堵塞了,通过上面的方法还没有发现是被谁堵塞了,就别慌了,也不用怀疑您自己的技能问题。
当前处理方法:
- 尽快取消或者kill执行alter语句的会话,以免造成堵塞队列,影响系统的可用性。
- 如果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.