MySQL事務(二)事務隔離的實現原理:一致性讀

今天咱們來學習一下MySQL的事務隔離是如何實現的。若是你對事務以及事務隔離級別還不太瞭解的話,這裏左轉html

好的,下面正式進入主題。事務隔離級別有4種:讀未提交、讀提交、可重複讀和串行化。首先咱們來講一下讀未提交和串行化。數據庫

  • 讀未提交:性能最好,由於不加鎖,因此能夠理解爲沒有隔離。
  • 串行化:讀加共享鎖,其餘事務可併發讀,但不能寫;寫加排他鎖,其餘事務不能併發寫也不能併發讀。

這兩種方式要麼啥都無論,併發性能最好,但也最多問題;要麼管得很嚴,沒法併發處理,實現簡單。數組

另外兩種,讀提交和可重複讀,的實現方式就有考究了。併發

可重複讀

首先咱們來看一下可重複讀是如何實現的。性能

在可重複讀隔離級別下,事務在啓動的時候就「拍了個快照」,而且這個快照是基於整個庫的。而「快照」在計算機裏是拷貝了一份當前的副本文件,但在數據庫併發訪問場景下,不可能真的拷貝一份數據副本。學習

實際上,這個快照是基於InnoDB在實現MVCC時用到的一致性讀視圖來實現的。日誌

MVCC的全稱是「多版本併發控制」。這項技術使得InnoDB的事務隔離級別下執行一致性讀操做有了保證,換言之,就是爲了查詢一些正在被另外一個事務更新的行,而且能夠看到它們被更新以前的值。這是一個能夠用來加強併發性的強大的技術,由於這樣的一來的話查詢就不用等待另外一個事務釋放鎖。這項技術在數據庫領域並非廣泛使用的。一些其它的數據庫產品,以及MySQL其它的存儲引擎並不支持它。htm

如何實現「快照」

InnoDB裏面每一個事務有一個惟一的事務ID,叫做transaction id。它是在事務開始的時候向InnoDB的事務系統申請的,是按申請順序嚴格遞增的。blog

數據表中的一行記錄,其實可能有多個版本(row),每一個版本都有本身的row trx_id。如圖1所示:事務


圖1 行狀態變動圖

圖中虛線框裏是同一行數據的4個版本,當前最新版本是V4,k的值是22,它是被transaction id爲25的事務更新的,所以它的row trx_id是25。

其實除了最新版本V4外,其餘三個版本其實是不存在的,它們是由undo log和最新版本數據計算獲得的。其中undo段就是圖中的虛線箭頭的U一、U二、U3,例如V1版本就是根據V4依次執行U三、U二、U1計算獲得的。

undo log是回滾日誌,保存的是邏輯格式的日誌,可用於事務回滾,也能夠用於MVCC。

按照可重複讀的定義,一個事務啓動的時候,可以看到全部已經提交的事務結果。但以後在這個事務執行期間,其餘事務的更新對它不可見。

InnoDB爲每一個事務構成一個數組,用來保存這個事務啓動瞬間,當前正在「活躍」的全部事務ID。「活躍」指啓動了但尚未提交。

數組裏事務ID的最小值記爲低水位,當前系統裏面已經建立過的事務ID的最大值加1記爲高水位。這個視圖數組加高水位就組成了當前事務的一致性視圖(read-view)。

而數據版本的可見性規則,就是基於數據的row trx_id和這個一致性視圖的對比結果獲得的。

對於當前事務的啓動瞬間來講,一個數據版本的row trx_id有如下幾種可能:

  • 若是落在綠色部分,表示這個版本是已經提交的事務或者是當前事務本身生成的,這個數據是可見的。
  • 若是落在紅色部分,表示這個版本是由未來啓動的事務生成的,不可見。
  • 若是落在橙色部分,就有兩種狀況:
    • 若row trx_id在數組中,表示這個版本是由還沒提交的事務生成的,不可見。
    • 若row trx_id不在數組中,表示這個版本是已經提交了的事務生成的,可見。

好比圖1中的數據來講,若是有一個事務,它的低水位是18,那麼當它訪問這一行數據時,就會從V4經過U3計算出V3,因此在它看來,這一行的值是11。

在更新時如何使用一致性讀


圖3 示例1

咱們來看示例1,若是事務B在事務C更新以前查詢,這個查詢返回值是1。可是當它要去更新數據時,就不能在歷史版本上更新了,不然事務事務C的更新就會丟失。

這裏就用到一條規則:更新數據都是先讀後寫,而這個讀只能是讀當前的值,稱爲「當前讀」(current read)。

所以事務B更新時,當前讀拿到的數據是(1, 2),更新後是(1, 3),而且row trx_id是101。

事務B後續查詢時,看到最新數據的版本號是101,而本身也是101,就直接返回,獲得的k值是3。


圖4 示例2

咱們再來看示例2,示例2與示例1的區別在於,事務C'在事務B的寫讀操做後提交。

事務C'在提交前對行加寫鎖。而事務B是當前讀,並且必需要加鎖,所以被鎖住了,必須等到事務C'釋放這個鎖,才能繼續它的當前讀。

到這裏,把一致性讀、當前讀和行鎖串起來了。

小結

本節問題,事務的可重複讀隔離級別是如何實現的?

可重複讀的核心就是一致性讀;而事務更新數據的時候,只能用當前讀。若是當前的記得的行鎖被其餘事務佔用的話,就須要進入鎖等待。

讀提交

讀提交的實現方式跟可重複讀相似,它們最主要的區別是:

  • 在可重複讀隔離級別下,只須要在事務開始的時候建立一致性視圖,以後事務裏的其餘查詢都共用這個一致性視圖;
  • 在讀提交隔離級別下,每一個語句執行前都會從新算出一個新的視圖。

參考資料

相關文章
相關標籤/搜索