MySQL技術內幕 InnoDB儲存引擎:鎖問題(髒讀、不可重複讀)
1、髒讀
在理解髒讀(Dirty Read)之前,需要理解髒資料的概念。但是髒資料和之前所介紹的髒頁完全是兩種不同的概念。髒頁指的是在緩衝池中已經被修改的頁,但是還沒有重新整理到磁碟中,即資料庫例項記憶體中的頁和磁碟中的頁的資料是不一致的,當然在重新整理到磁碟之前,日誌都已經被寫人到了重做日誌檔案中。而所謂髒資料是指事務對緩衝池中行記錄的修改,並且還沒有被提交(commit)。
對於髒頁的讀取,是非常正常的。髒頁是因為資料庫例項記憶體和磁碟的非同步造成的,這並不影響資料的一致性(或者說兩者最終會達到一致性,即當髒頁都刷回到磁碟)。並且因為髒頁的重新整理是非同步的,不影響資料庫的可用性,因此可以帶來效能的提高。
髒資料卻截然不同,髒資料是指未提交的資料,如果讀到了髒資料,即一個事務可以讀到另外一個事務中未提交的資料,則顯然違反了資料庫的隔離性。 ♦
髒讀指的就是在不同的事務下,當前事務可以讀到另外事務未提交的資料,簡單來說就是可以讀到髒資料。表6-15的例子顯示了一個髒讀的例子。
表t為我們之前在6.4.1中建立的表,不同的是在上述例子中,事務的隔離級別進行了更換,由預設的REPEATABLE READ換成了 READ UNCOMMITTED。因此在會話A中,在事務並沒有提交的前提下,會話B中的兩次SELECT操作取得了不同的結果,並且2這條記錄是在會話A中並未提交的資料,即產生了髒讀,違反了事務的隔離性。
髒讀現象在生產環境中並不常發生,從上面的例子中就可以發現,髒讀發生的
條件是需要事務的隔離級別為READ UNCOMMITTED,而目前絕大部分的資料庫都至少設定成 READ COMMITTED。 InnoDB儲存引擎預設的事務隔離級別為 READ REPEATABLE, Microsoft SQL Server 資料庫為 READ COMMITTED, Oracle 資料庫同樣也是 READ COMMITTED。
髒讀隔離看似毫無用處,但在一些比較特殊的情況下還是可以將事務的隔離級別設定為READ UNCOMMITTED。例如replication環境中的slave節點,並且在該slave上的查詢並不需要特別精確的返回值。
2、不可重複讀
不可重複讀是指在一個事務內多次讀取同一資料集合。在這個事務還沒有結束時,另外一個事務也訪問該同一資料集合,並做了一些DML操作。因此,在第一個事務中的兩次讀資料之間,由於第二個事務的修改,那麼第一個事務兩次讀到的資料可能是不一樣的。這樣就發生了在一個事務內兩次讀到的資料是不一樣的情況,這種情況稱為不可重複讀。
不可重複讀和髒讀的區別是:髒讀是讀到未提交的資料,而不可重複讀讀到的卻是已經提交的資料,但是其違反了資料庫事務一致性的要求◊可以通過下面一個例子來觀察不可重複讀的情況,如表6-16所示。
在會話A中開始一個事務,第一次讀取到的記錄是1,在另一個會話B中開始了
另一個事務,插人一條為2的記錄,在沒有提交之前,對會話A中的事務進行再次讀取時,讀到的記錄還是1,沒有發生髒讀的現象。但會話B中的事務提交後,在對會話A中的事務進行讀取時,這時讀到是1和2兩條記錄。這個例子的前提是,在事務開始前,會話A和會話B的事務隔離級別都調整為READ COMMITTED。
一般來說,不可重複讀的問題是可以接受的,因為其讀到的是已經提交的資料,本身並不會帶來很大的冋題。因此,很多資料庫廠商(如Oracle、 Microsoft SQL Server)將其資料庫事務的預設隔離級別設定為READ COMMITTED。在這種隔離級別允許不可重複讀的現象。
在InnoDB儲存引擎中,通過使用Next-Key Lock演算法來避免不可重複讀的問題。在MySQL官方文件中將不可重複讀的問題定義為Phantom Problem,即幻像問題。在NextKey Lock 演算法下,對於索引的掃描,不僅是鎖住掃描到的索引,而且還鎖住這些索引覆蓋的範圍(gap)。因此在這個範圍內的插入都是不允許的。這樣就避免了另外的事務在這個範圍內插人資料導致的不可重複讀的問題。因此,InnoDB儲存引擎的預設事務隔離級別是READ REPEATABLE,採用Next-Key Lock演算法,避免了不可重複讀的現象。
本文整理自:《MySQL技術內幕 InnoDB儲存引擎》
個人微信公眾號:
作者:jiankunking 出處:http://blog.csdn.net/jiankunking