MySQL在5.5.3版本引入了metadata lock
他的本意是解决以前版本事务隔离特性的几个bug,可是引入的问题也不小.
先说说MySQL的事务吧.
Oracle的事务指的是须要分配回滚段的SQL语句,也就是说select并非oracle事务的一部分.
好比运行一个查询,而后在另一个会话查询v$transaction,并不会有任何相关的信息.直到事务中出现insert,update,delete。
而innodb的事务包括select查询.
不管事务隔离级别是可重复读,仍是读提交,只要有查询,事务就开始了
下图证实了在5.6.15,设置了autocommit=0以后,运行一个查询就能够开启一个事务.
第一个会话运行查询.
第二个会话,运行 show engine innodb status\G 查看事务状况
能够看到id为1的线程,已经开始了一个事务.
为何Oracle的事务仅包括insert,update和delete的语句,而innodb的事务包括全部的语句呢?
我以为这个和厂商支持的隔离级别有很大的关系.
众所周知,Oracle仅仅支持读提交和串行化两种事务隔离级别,而读提交是绝大多数数据库的选择.
读提交意味着能够出现幻读和不可重复读,那么从实现原理的角度,Oracle能够在语句(Statement级别)开始的时候,记录SCN而后应用MVCC查询.每一个查询只须要记录本身开始的SCN便可.而语句开始的SCN和事务并无关系.因此Oracle的事务,并不包括查询.
而innodb支持可重复读隔离级别,也就是说在一个事务中,不管运行多少次查询,结果都必须是一致的.
(innodb不只支持可重复读,而且使用间隙锁在可重复读级别避免了幻读,固然这也带来了不少问题..)
因此它记录的不是每一个查询语句的LSN,而是事务第一个语句发生时的LSN,不管第一个语句是查询,仍是修改.
innodb在可重复读的级别下,查询用事务开始时的LSN应用MVCC,与Oracle不一样的是,innodb查询回滚段中小于事务开始的LSN的数据版本,
而oracle查询回滚段中小于语句SCN的数据版本.
也就是说,一样都是MVCC,oracle是语句级的,innodb是事务级的
这里有一个问题,按说事务包括查询是由于可重复读隔离级别的须要,可是innodb读提交隔离级别一样也将查询做为了事务的一部分.
多是由于架构或者代码实现层面的问题吧.
无论怎么样,Innodb就是这么作了.
而后再说说metadata lock
在5.5.3以前,metadata lock是语句级的,这实际上破坏了事务的一致性.
好比一个事务,在可重复读隔离级别,运行两次查询,竟然结果不一致.
这正是由于metadata lock是语句级形成的问题,
在两个查询的间隔,另一个会话执行了truncate table.
因此再次运行查询,没有任何结果.
MySQL为了解决这个问题,在5.5.3将metadata lock提高为事务级别的锁.
任何DDL都须要先得到metadata lock,可是这个锁须要等事务结束的时候释放.
一样的实验,在5.6.13就变成这样的了.
第一个会话的事务没有结束,那么第二个会话的DDL就被阻塞
使用show processlist能够看到DDL语句在等待第一个会话事务的metadata lock
经过这种方式,就保证了可重复读隔离级别下,事务的一致性.
和以前提到的查询也做为事务的一部分同样,innodb并无为读提交量身定制一些东西,
好比读提交并不须要查询做为事务的一部分
和读提交并不须要事务级别的metadata lock.
多是出于架构层面的问题,不少可重复读的特性强加在了读提交上,
因此一旦这些特性出现问题,即便将隔离级别降为读提交也不能避免.
接下来问题来了,
刚才的DDL被metadata lock阻塞,这个DDL还会进一步阻塞其余的事务.甚至是查询(查询是innodb事务的一部分.)
这就有点抓狂了,由于这个时候,系统其实已经Hung了.
假设id为1的线程持有metadata lock 没有提交,
id为2的线程进行DDL,而后被阻塞在线程1的metadata锁上,
这时,数据库依次来了8个查询,他们都阻塞在了线程2上.
假如线程1的事务不结束,其余的线程都被阻塞.
即便线程1的事务结束了..也是后面8个事务依次得到metadata锁,与此同时,这个DDL可能又阻塞了80个事务..
这时候,系统的并发为1,这个DDL可能永远不能执行.而且这种状况不在死锁检测的范围内.
它的锁超时时间,由lock_wait_timeout参数控制,默认是31536000(一年,坑爹吧)
MySQL虽然保证了事务的一致性,避免了bug,可是引入的问题却可能让我这样的初级dba丢了饭碗..
最后梳理一下可能引起metadata lock连环阻塞的状况
1.在有其余事务运行的时候,进行DDL操做(alter table;truncate;)
2.在mysqldump运行的时候,进行DDL操做.(想一想就以为坑爹)
3.在Master-Slave复制环境,在Slave运行查询,会致使Master传过来的DDL阻塞.致使复制延迟增大.
4.建立索引(...)
做为初级dba来讲,为了保住饭碗,能够有两个动做
1.将lock_wait_timeout参数调低
2.在运行DDL以前,查看事务是否频繁,在运行DDL以后,开启另一个会话,使用show processlist查看是否被metadata lock阻塞.
一旦阻塞,先Kill ddl的操做.
参考:
http://blog.csdn.net/wzy0623/article/details/42149525
http://blog.csdn.net/wzy0623/article/details/8679457
http://blog.itpub.net/26515977/viewspace-1208250/mysql