概述
今天主要分享下MySQL事務隔離級别的實作原理,因為隻有InnoDB支援事務,是以這裡的事務隔離級别是指InnoDB下的事務隔離級别。

隔離級别
- 讀未送出:一個事務可以讀取到另一個事務未送出的修改。這會帶來髒讀,幻讀,不可重複讀問題
- 讀已送出:一個事務隻能讀取另一個事務已經送出的修改。其避免了髒讀,仍然存在不可以重複讀和幻讀問題
- 可重複讀:同一個事務中多次讀取相同的資料傳回的結果是一樣的。其避免了髒讀和不可重複讀問題,但是幻讀依然存在
- 串行化:事務串行之行。避免了以上所有問題
以上是SQL-92标準中定義的四種隔離級别。在MySQL中,預設的隔離級别是REPEATABLE-READ(可重複讀),并且解決了幻讀問題。
注:不可重複讀重點在于Update和delete,而幻讀的重點在于insert。
MVCC
MVCC的全稱是多版本并發控制。MVCC使得InnoDB的事務隔離級别下執行一緻性讀操作有了保證。
簡單說就是為了查詢一些正在被另一個事務更新的行,并且可以看到它們被更新之前的值。這是一個用來增強并發性的強大技術,可以使得查詢不用等待另一個事務釋放鎖。
如下圖所示:
MVCC會給每一行增加三個字段,分别是:DB-TRX-ID、DB-ROLL-PTR、DB-ROW-ID
增删查改
在InnoDB中,給每行增加兩個隐藏字段來實作MVCC,一個用來記錄資料行的建立時間,另一個用來記錄行的過期時間。
在實際操作中,存儲的并不是時間,而是事務版本号,每開啟一個新事務,事務的版本号就會遞增。是以增删改查中對版本号的作用如下:
select:
讀取建立版本小于或等于目前事務版本号,并且删除版本為空或大于目前事務版本的記錄。這樣可以保證在讀取之前記錄都是存在的
insert:
将目前事務的版本号儲存至行的建立版本号
update
新插入一行,并以目前事務版本号作為新行的建立版本号,同時将原記錄行的删除版本号設定為目前事務版本号
delete
将目前事務版本号儲存至行的删除版本号
快照讀和目前讀
- 快照讀:讀取的是快照版本,也就是曆史版本
- 目前讀:讀取的是最新版版
普通的 select 就是快照讀,而 update,delete,insert,select...LOCK In SHARE MODE,SELECT...for update 就是目前讀
一緻性非鎖定讀和鎖定讀
首先看看下面的圖:
1、鎖定讀
在一個事務中,标準的SELECT語句是不會加鎖,但是有兩種情況例外。
- SELECT ... LOCK IN SHARE MODE
- SELECT ... FOR UPDATE
SELECT ... LOCK IN SHARE MODE:給記錄假設共享鎖,這樣其他事務職能讀不能修改,直到目前事務送出
SELECT ... FOR UPDATE:給索引記錄加鎖,這種情況跟UPDATE的加鎖情況是一樣的
2、一緻性非鎖定讀
consistent read(一緻性讀),InnoDB用多版本來提供查詢資料庫在某個時間點的快照。
如果隔離級别是REPEATABLE READ,那麼在同一個事務中的所有一緻性讀都讀的是事務中第一個的讀讀到的快照;如果是READ COMMITTED,那麼一個事務中的每一個一緻性讀都會讀到它自己重新整理的快照版本。
consistent read(一緻性讀)是READ COMMITTED和REPEATABLE READ隔離級别下普通SELECT語句預設的模式。一緻性讀不會給它鎖通路的表加任何形式的鎖,是以其他事務可以同時并發的修改它們
鎖
- Record Locks(記錄鎖):在索引記錄上加鎖
- Gap Locks(間隙鎖):在索引記錄之間加鎖,或者在第一個索引記錄之前加鎖,或者在最後一個索引記錄之後加鎖
- Next-Key Locks:在索引記錄上加鎖,并且在索引記錄之前的間隙加鎖。相當于Record Locks與Gap Locks的一個結合
假如一個索引包含以下幾個值:10,11,13,20.那麼這個索引的next-key鎖将會覆寫以下區間:
(negative infinity, 10]
(10, 11]
(11, 13]
(13, 20]
(20, positive infinity)
總結
在預設的隔離級别中,普通的SELECT用的是一緻性讀不加鎖。而對于鎖定讀,UPDATE和DELETE,則需要加鎖,至于加什麼鎖是有不同情況的。
如果對一個唯一索引使用了唯一的檢索條件,那麼隻需要鎖定相應的索引記錄就好;如果是沒有使用唯一索引作為檢索條件,或者用到了索引範圍掃描,那麼将會使用間隙鎖或者next-key鎖來以此阻塞其他會話向這個範圍内的間隙插入資料
利用MVCC實作一緻性非鎖定讀,保證在同一個事務中多次讀取相同的資料傳回的結果是一樣的,解決了不可重複讀問題.
利用Gap Locks和Next-key可以阻止其他事務在鎖定區間内插入資料,解決了幻讀問題.
總之,MySQL的預設隔離級别的實作依賴于MVCC和鎖,準确點說就是一緻性讀和鎖。