快照讀,當前讀——InnoDB在Read Repeat隔離級別下的事件處理

一、事務的隔離級別

髒讀:髒讀是指在一個事務處理過程裏讀取了另一個未提交的事務中的數據。

不可重複讀:不可重複讀是指在對於數據庫中的某個數據,一個事務範圍內多次查詢卻返回了不同的數據值,這是由於在查詢間隔,被另一個事務修改並提交了。

幻讀:事務1查詢某個範圍的數據時,事務2在該範圍內插入新的數據(或者刪除),事務1再次查詢該範圍時(未必是和之前同樣的查詢語句),返回不同的結果集。

MySQL數據庫爲我們提供的四種隔離級別:

  ① Serializable (串行化):可避免髒讀、不可重複讀、幻讀的發生。

  ② Repeatable read (可重複讀):可避免髒讀、不可重複讀的發生。

  ③ Read committed (讀已提交):可避免髒讀的發生。

  ④ Read uncommitted (讀未提交):最低級別,任何情況都無法保證。

實際上,mysql在可重複讀級別下,利用MVCC(多版本併發控制)及臨鍵鎖等在一定程度上解決了幻讀的問題(一部分幻讀的情況)。

二、MVCC原理及實現

(1)數據結構

 在Mysql中MVCC是在Innodb存儲引擎中得到支持的,Innodb爲每行記錄都實現了三個隱藏字段:

6字節的事務ID(DB_TRX_ID )
7字節的回滾指針(DB_ROLL_PTR)
隱藏的ID
6字節的事物ID用來標識該行所述的事務,7字節的回滾指針需要了解下Innodb的事務模型。

 

(2)事務日誌
爲了支持事務,Innbodb引入了下面幾個概念:
redo log:redo log就是保存執行的SQL語句到一個指定的Log文件,當Mysql執行recovery時重新執行redo log記錄的SQL操作即可。當客戶端執行每條SQL(更新語句)時,redo log會被首先寫入log buffer;當客戶端執行COMMIT命令時,log buffer中的內容會被視情況刷新到磁盤。redo log在磁盤上作爲一個獨立的文件存在,即Innodb的log文件。
undo log:與redo log相反,undo log是爲回滾而用,具體內容就是copy事務前的數據庫內容(行)到undo buffer,在適合的時間把undo buffer中的內容刷新到磁盤。undo buffer與redo buffer一樣,也是環形緩衝,但當緩衝滿的時候,undo buffer中的內容會也會被刷新到磁盤;與redo log不同的是,磁盤上不存在單獨的undo log文件,所有的undo log均存放在主ibd數據文件中(表空間),即使客戶端設置了每表一個數據文件也是如此。
rollback segment:回滾段這個概念來自Oracle的事物模型,在Innodb中,undo log被劃分爲多個段,具體某行的undo log就保存在某個段中,稱爲回滾段。可以認爲undo log和回滾段是同一意思。
 

下面演示下事務對某行記錄的更新過程:
1. 初始數據行

F1~F6是某行列的名字,1~6是其對應的數據。後面三個隱含字段分別對應該行的事務號和回滾指針,假如這條數據是剛INSERT的,可以認爲ID爲1,其他兩個字段爲空。
2.事務1更改該行的各字段的值

當事務1更改該行的值時,會進行如下操作:
用排他鎖鎖定該行
記錄redo log
把該行修改前的值Copy到undo log,即上圖中下面的行
修改當前行的值,填寫事務編號,使回滾指針指向undo log中的修改前的行
3.事務2修改該行的值

與事務1相同,此時undo log,中有有兩行記錄,並且通過回滾指針連在一起。
因此,如果undo log一直不刪除,則會通過當前記錄的回滾指針回溯到該行創建時的初始內容,所幸的時在Innodb中存在purge線程,它會查詢那些比現在最老的活動事務還早的undo log,並刪除它們,從而保證undo log文件不至於無限增長。
Innodb的實現實際上並不是純粹的MVCC,因爲並沒有實現核心的多版本共存,undo log中的內容只是串行化的結果,記錄了多個事務的過程,不屬於多版本共存。但理想的MVCC是難以實現的,當事務僅修改一行記錄使用理想的MVCC模式是沒有問題的,可以通過比較版本號進行回滾;但當事務影響到多行數據時,理想的MVCC據無能爲力了。

這裏有一點要注意的是,在MVCC模式下,只有進行讀操作才能保證不出現幻讀的情況(因爲對數據進行了快照),如果需要對數據進行更新、插入、刪除操作,這些操作會轉爲當前讀。特別是如果這些操作在快照數據範圍內,有可能導致快照數據被更新(出現別的事務的操作結果)。比如事務T1讀取出某行,事務T2對該行的字段2進行了更新並提交,事務T1再對該行的字段1進行更新。此時事務T1再讀取該行,不僅會展示字段1更新後的結果,還會展示字段2更新的結果。
 

三、臨鍵鎖

臨鍵鎖其實是行鎖和間隙鎖的組合。
Record Lock(行鎖):    在索引上對單行記錄加鎖.
Gap Lock(間隙鎖):    鎖定一個範圍的記錄,但不包括記錄本身.鎖加在未使用的空閒空間上,可能是兩個索引記錄之間,也可能是第一個索引記錄之前或最後一個索引之後的空間.
Next-Key Lock(臨鍵鎖):    行鎖與間隙鎖組合起來用就叫做Next-Key Lock。鎖定一個範圍,並且鎖定記錄本身。對於行的查詢,都是採用該方法,主要目的是解決幻讀的問題。
對於臨鍵渙,準確的理解是,當隔離級別是可重複讀,且禁用innodb_locks_unsafe_for_binlog的情況下,在搜索和掃描index的時候使用的next-key locks可以避免幻讀。

上圖是一個臨鍵鎖的簡單示例,它不僅會鎖住相應的行記錄,還會鎖住相應的範圍。這裏有一點要注意的是,僅在非唯一索引下等值查詢會做如下處理。如果命中的是唯一索引,等值查詢會只鎖定相應的行,範圍查詢纔會鎖定相應的間隙鎖和行鎖。通過獲得相應的間隙鎖及行鎖,臨鍵鎖可以避免其它事務對鎖範圍內的記錄進行操作(更新、插入、刪除)從而避免鎖定範圍內的幻讀。

從以上理解可以看出,臨鍵鎖要避免幻讀,需要在相應的記錄上加上相應的行鎖和間隙鎖。如果事務T1的查詢條件下,並沒有相應的行及範圍,那麼也就無法對相應的索引行(非唯一索引)及記錄行加鎖。此時,事務T2在該條件下插入記錄,事務T1再進行該條件的查詢,則可以看到新插入的記錄(幻讀)。

相關資料收集

https://blog.csdn.net/luzhensmart/article/details/88134189

InnoDB的MVCC如何解決不可重複讀和快照讀的幻讀,當前讀用next-key解決幻讀

https://blog.csdn.net/ymybxx/article/details/80496313

mysql爲什麼是部分解決幻讀

https://blog.csdn.net/ashic/article/details/53735537

Mysql(Innodb)如何避免幻讀

https://blog.csdn.net/chen77716/article/details/6742128

Mysql中的MVCC

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章