READ-COMMITED 與 REPEATABLE-READ 事務隔離級別之間的異同

常常會被問到 InnoDB隔離級別中 READ-COMMITED和REPEATABLE-READ 的區別,今天就整理了一下,再也不從「髒讀」、「幻讀」這樣的名詞解釋同樣去回答了。mysql

1. 行鎖

InnoDB行鎖實際鎖的是索引記錄,爲了防止死鎖的產生以及維護所須要的隔離級別,在執行sql語句的全過程當中,innodb必須對所須要修改的行每條索引記錄上鎖。如此一來,若是你執行的 UPDATE 沒有很好的索引,那麼會致使鎖定許多行:sql

update employees set store_id = 0 where store_id = 1;
---TRANSACTION 1EAB04, ACTIVE 7 sec
633 lock struct(s), <strong>heap size 96696</strong>, 218786 row lock(s), undo log entries 1
MySQL thread id 4, OS thread handle 0x7f8dfc35d700, query id 47 localhost root
show engine innodb status

上面的 employeesstore_id 列沒有索引。注意 UPDATE 已經執行完成(沒有提交),但依然有 218786 個行鎖沒有釋放,還有一個undo記錄。這意味着只有一行被更改,但卻持有了額外的鎖。堆大小(heap size)表明了分配給鎖使用的內存數量。code

在 REPEATABLE-READ 級別,事務持有的 每一個鎖 在整個事務期間一直被持有。對象

在 READ-COMMITED 級別,事務裏面特定語句結束以後,不匹配該sql語句掃描條件的鎖,會被釋放。blog

下面是上述相同的 UPDATE 在 READ-COMMITED 級別下的結果:索引

---TRANSACTION 1EAB06, ACTIVE 11 sec
631 lock struct(s), <strong>heap size 96696</strong>, 1 row lock(s), undo log entries 1
MySQL thread id 4, OS thread handle 0x7f8dfc35d700, query id 62 localhost root
show engine innodb status

能夠看到 heap size 沒有變化,可是如今咱們只持有一個行鎖。不管什麼隔離級別下,InnoDB 會爲掃描過的每條索引記錄建立鎖,不一樣的是在 RC 模式,一旦語句執行完畢(事務未必完成),不符合掃描條件的記錄上的鎖會被隨即釋放。釋放這些鎖後,堆內存並不會立刻釋放,因此heap size看到與 RR 模式是同樣的,可是持有的鎖數量明顯小了不少。事務

這也就意味着在 RC 級別下的事務A,只要A的UPDATE 語句 完成了,其它事務能夠修改A中也掃描過的行,但在 RR 級別下不容許。ip

2. Read View

REPEATABLE-READ

在 REPEATABLE-READ 級別,read view 對象在事務一開啓就被建立,這個一致性快照在整個事務期間一直保持打開。在同一個事務裏,先後間隔幾個小時執行一遍相同的 SELECT,你會獲得徹底同樣的結果,這就是所謂的 MVCC (multiple version concurrency control),它是經過行版本號和UNDO段來實現的。內存

在 REPEATABLE-READ 級別, InnoDB會爲範圍掃描建立間隙鎖(gap locks):get

select * from some_table where id > 100 FOR UPDATE;

上面的update將會建立一個 gap lock,用來防止在 id>100 範圍內有新行被插入,鎖會持續到事務回滾或提交。好比在同一個事務裏,上午5點執行 SELECT ... FOR UPDATE,下午5點執行 UPDATE some_table where id>100,那麼這個update只會修改上午5點 SELECT FOR UPDATE所鎖定的行,由於大於100的記錄的整個 間隙 被加了鎖。

READ-COMMITED

在 READ-COMMITED 級別,read view 結構在每一個語句開始的時候被建立,這意味着即便在同一個事務中,上午5點執行的 SELECT與下午5點執行的SELECT可能會獲得不一樣的結果。由於 read view 在 READ-COMMITED 級別下僅在 語句執行 期間存在。

這就是所謂的 「幻讀」(phantom read)。

READ-COMMITED 隔離級別下是沒有gap locks,因此執行上面的 SELECT FOR UPDATE where id>100 並不會阻止其它事務插入新行,若是同一個事務裏後面執行 UPDATE ... where id>100,就有可能致使實際更新的行數比前面鎖定的行數要多。

補充:
若是瞭解過 mysqldump 的實現原理,可知它就是充分利用InnoDB的MVCC特性,使用 REPEATABLE-READ 模式獲取備份事務的一致性快照,避免鎖表和幻讀。

本文主要參考自 percona博客上的一篇文章 https://www.percona.com/blog/...


本文連接地址:http://seanlook.com/2016/09/0...

相關文章
相關標籤/搜索