今天咱們來學習一下MySQL的事務隔離是如何實現的。若是你對事務以及事務隔離級別還不太瞭解的話,這裏左轉。html
好的,下面正式進入主題。事務隔離級別有4種:讀未提交、讀提交、可重複讀和串行化。首先咱們來講一下讀未提交和串行化。數據庫
這兩種方式要麼啥都無論,併發性能最好,但也最多問題;要麼管得很嚴,沒法併發處理,實現簡單。數組
另外兩種,讀提交和可重複讀,的實現方式就有考究了。併發
首先咱們來看一下可重複讀是如何實現的。性能
在可重複讀隔離級別下,事務在啓動的時候就「拍了個快照」,而且這個快照是基於整個庫的。而「快照」在計算機裏是拷貝了一份當前的副本文件,但在數據庫併發訪問場景下,不可能真的拷貝一份數據副本。學習
實際上,這個快照是基於InnoDB在實現MVCC時用到的一致性讀視圖來實現的。日誌
MVCC的全稱是「多版本併發控制」。這項技術使得InnoDB的事務隔離級別下執行一致性讀操做有了保證,換言之,就是爲了查詢一些正在被另外一個事務更新的行,而且能夠看到它們被更新以前的值。這是一個能夠用來加強併發性的強大的技術,由於這樣的一來的話查詢就不用等待另外一個事務釋放鎖。這項技術在數據庫領域並非廣泛使用的。一些其它的數據庫產品,以及MySQL其它的存儲引擎並不支持它。htm
InnoDB裏面每一個事務有一個惟一的事務ID,叫做transaction id。它是在事務開始的時候向InnoDB的事務系統申請的,是按申請順序嚴格遞增的。blog
數據表中的一行記錄,其實可能有多個版本(row),每一個版本都有本身的row trx_id。如圖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有如下幾種可能:
好比圖1中的數據來講,若是有一個事務,它的低水位是18,那麼當它訪問這一行數據時,就會從V4經過U3計算出V3,因此在它看來,這一行的值是11。
咱們來看示例1,若是事務B在事務C更新以前查詢,這個查詢返回值是1。可是當它要去更新數據時,就不能在歷史版本上更新了,不然事務事務C的更新就會丟失。
這裏就用到一條規則:更新數據都是先讀後寫,而這個讀只能是讀當前的值,稱爲「當前讀」(current read)。
所以事務B更新時,當前讀拿到的數據是(1, 2),更新後是(1, 3),而且row trx_id是101。
事務B後續查詢時,看到最新數據的版本號是101,而本身也是101,就直接返回,獲得的k值是3。
咱們再來看示例2,示例2與示例1的區別在於,事務C'在事務B的寫讀操做後提交。
事務C'在提交前對行加寫鎖。而事務B是當前讀,並且必需要加鎖,所以被鎖住了,必須等到事務C'釋放這個鎖,才能繼續它的當前讀。
到這裏,把一致性讀、當前讀和行鎖串起來了。
本節問題,事務的可重複讀隔離級別是如何實現的?
可重複讀的核心就是一致性讀;而事務更新數據的時候,只能用當前讀。若是當前的記得的行鎖被其餘事務佔用的話,就須要進入鎖等待。
讀提交的實現方式跟可重複讀相似,它們最主要的區別是: