多版本併發控制(Multi-Version Concurrency Control, MVCC)是 MySQL 的 InnoDB 存儲引擎實現隔離級別的一種具體方式,用於實現提交讀和可重複讀這兩種隔離級別。而未提交讀隔離級別老是讀取最新的數據行,無需使用 MVCC。可串行化隔離級別須要對全部讀取的行都加鎖,單純使用 MVCC 沒法實現。sql
MVCC 在每行記錄後面都保存着兩個隱藏的列,用來存儲兩個版本號:併發
MVCC 使用到的快照存儲在 Undo 日誌中,該日誌經過回滾指針把一個數據行(Record)的全部快照鏈接起來。spa
如下實現過程針對可重複讀隔離級別。指針
當開始新一個事務時,該事務的版本號確定會大於當前全部數據行快照的建立版本號,理解這一點很關鍵。日誌
多個事務必須讀取到同一個數據行的快照,而且這個快照是距離如今最近的一個有效快照。可是也有例外,若是有一個事務正在修改該數據行,那麼它能夠讀取事務自己所作的修改,而不用和其它事務的讀取結果一致。code
把沒有對一個數據行作修改的事務稱爲 T,T 所要讀取的數據行快照的建立版本號必須小於 T 的版本號,由於若是大於或者等於 T 的版本號,那麼表示該數據行快照是其它事務的最新修改,所以不能去讀取它。除此以外,T 所要讀取的數據行快照的刪除版本號必須大於 T 的版本號,由於若是小於等於 T 的版本號,那麼表示該數據行快照是已經被刪除的,不該該去讀取它。blog
將當前系統版本號做爲數據行快照的建立版本號。索引
將當前系統版本號做爲數據行快照的刪除版本號。事務
將當前系統版本號做爲更新前的數據行快照的刪除版本號,並將當前系統版本號做爲更新後的數據行快照的建立版本號。能夠理解爲先執行 DELETE 後執行 INSERT。it
使用 MVCC 讀取的是快照中的數據,這樣能夠減小加鎖所帶來的開銷。
select * from table ...;
讀取的是最新的數據,須要加鎖。如下第一個語句須要加 S 鎖,其它都須要加 X 鎖。
select * from table where ? lock in share mode; select * from table where ? for update; insert; update; delete;
Next-Key Locks 是 MySQL 的 InnoDB 存儲引擎的一種鎖實現。
MVCC 不能解決幻讀的問題,Next-Key Locks 就是爲了解決這個問題而存在的。在可重複讀(REPEATABLE READ)隔離級別下,使用 MVCC + Next-Key Locks 能夠解決幻讀問題。
鎖定一個記錄上的索引,而不是記錄自己。
若是表沒有設置索引,InnoDB 會自動在主鍵上建立隱藏的聚簇索引,所以 Record Locks 依然可使用。
鎖定索引之間的間隙,可是不包含索引自己。例如當一個事務執行如下語句,其它事務就不能在 t.c 中插入 15。
SELECT c FROM t WHERE c BETWEEN 10 and 20 FOR UPDATE;
它是 Record Locks 和 Gap Locks 的結合,不只鎖定一個記錄上的索引,也鎖定索引之間的間隙。例如一個索引包含如下值:10, 11, 13, and 20,那麼就須要鎖定如下區間:
(negative infinity, 10] (10, 11] (11, 13] (13, 20] (20, positive infinity)