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