MySQL鎖知識點總結
MySQL鎖知識點總結
鎖型別
共享鎖(S Lock)
允許事務讀一行資料
排他鎖(X Lock)
允許事務刪除或者更新一行資料
意向共享鎖(IS Lock)
事務想要獲得一張表中某幾行的共享鎖
意向排他鎖
事務想要獲得一張表中某幾行的排他鎖
MVCC
多版本併發控制(Multi-Version Concurrency Control, MVCC)是 MySQL 的 InnoDB 儲存引擎實現隔離級別的一種具體方式,用於實現提交讀和可重複讀這兩種隔離級別。而未提交讀隔離級別總是讀取最新的資料行,無需使用 MVCC。可序列化隔離級別需要對所有讀取的行都加鎖,單純使用 MVCC 無法實現。
基礎概念
版本號
系統版本號:是一個遞增的數字,每開始一個新的事務,系統版本號就會自動遞增。
事務版本號:事務開始時的系統版本號。
隱藏的列
MVCC 在每行記錄後面都儲存著兩個隱藏的列,用來儲存兩個版本號:
建立版本號:指示建立一個數據行的快照時的系統版本號;
刪除版本號:如果該快照的刪除版本號大於當前事務版本號表示該快照有效,否則表示該快照已經被刪除了。
Undo 日誌
MVCC 使用到的快照儲存在 Undo 日誌中,該日誌通過回滾指標把一個數據行(Record)的所有快照連線起來。
實現過程
以下實現過程針對可重複讀隔離級別。
當開始一個事務時,該事務的版本號肯定大於當前所有資料行快照的建立版本號,理解這一點很關鍵。資料行快照的建立版本號是建立資料行快照時的系統版本號,系統版本號隨著建立事務而遞增,因此新建立一個事務時,這個事務的系統版本號比之前的系統版本號都大,也就是比所有資料行快照的建立版本號都大。
SELECT
多個事務必須讀取到同一個資料行的快照,並且這個快照是距離現在最近的一個有效快照。但是也有例外,如果有一個事務正在修改該資料行,那麼它可以讀取事務本身所做的修改,而不用和其它事務的讀取結果一致。
把沒有對一個數據行做修改的事務稱為 T,T 所要讀取的資料行快照的建立版本號必須小於等於 T 的版本號,因為如果大於 T 的版本號,那麼表示該資料行快照是其它事務的最新修改,因此不能去讀取它。除此之外,T 所要讀取的資料行快照的刪除版本號必須是未定義或者大於 T 的版本號,因為如果小於等於 T 的版本號,那麼表示該資料行快照是已經被刪除的,不應該去讀取它。
INSERT
將當前系統版本號作為資料行快照的建立版本號。
DELETE
將當前系統版本號作為資料行快照的刪除版本號。
UPDATE
將當前系統版本號作為更新前的資料行快照的刪除版本號,並將當前系統版本號作為更新後的資料行快照的建立版本號。可以理解為先執行 DELETE 後執行 INSERT。
快照讀與當前讀
在可重複讀級別中,通過MVCC機制,雖然讓資料變得可重複讀,但我們讀到的資料可能是歷史資料,是不及時的資料,不是資料庫當前的資料!這在一些對於資料的時效特別敏感的業務中,就很可能出問題。
對於這種讀取歷史資料的方式,我們叫它快照讀 (snapshot read),而讀取資料庫當前版本資料的方式,叫當前讀 (current read)。很顯然,在MVCC中:
快照讀
MVCC 的 SELECT 操作是快照中的資料,不需要進行加鎖操作。
select * from table ….;
當前讀
MVCC 其它會對資料庫進行修改的操作(INSERT、UPDATE、DELETE)需要進行加鎖操作,從而讀取最新的資料。可以看到 MVCC 並不是完全不用加鎖,而只是避免了 SELECT 的加鎖操作。
INSERT;
UPDATE;
DELETE;
在進行 SELECT 操作時,可以強制指定進行加鎖操作。以下第一個語句需要加 S 鎖,第二個需要加 X 鎖。
- select * from table where ? lock in share mode;
- select * from table where ? for update;
事務的隔離級別實際上都是定義的當前讀的級別,MySQL為了減少鎖處理(包括等待其它鎖)的時間,提升併發能力,引入了快照讀的概念,使得select不用加鎖。而update、insert這些“當前讀”的隔離性,就需要通過加鎖來實現了。
鎖演算法
Record Lock
單個行記錄上的鎖。
鎖定一個記錄上的索引,而不是記錄本身。
如果表沒有設定索引,InnoDB 會自動在主鍵上建立隱藏的聚簇索引,因此 Record Locks 依然可以使用。
Gap Lock
間隙鎖,鎖定一個範圍,不包括記錄本身。
鎖定索引之間的間隙,但是不包含索引本身。例如當一個事務執行以下語句,其它事務就不能在 t.c 中插入 15。
SELECT c FROM t WHERE c BETWEEN 10 and 20 FOR UPDATE;
Next-Key Lock
record+gap 鎖定一個範圍,包含記錄本身。
它是 Record Locks 和 Gap Locks 的結合,不僅鎖定一個記錄上的索引,也鎖定索引之間的間隙。例如一個索引包含以下值:10, 11, 13, and 20,那麼就需要鎖定以下區間:
(-∞, 10]
(10, 11]
(11, 13]
(13, 20]
(20, +∞)
在 InnoDB 儲存引擎中,SELECT 操作的不可重複讀問題通過 MVCC 得到了解決,而 UPDATE、DELETE 的不可重複讀問題通過 Record Lock 解決,INSERT 的不可重複讀問題是通過 Next-Key Lock(Record Lock + Gap Lock)解決的。
相關知識點:
innodb對於行的查詢使用next-key lock
Next-locking keying為了解決Phantom Problem幻讀問題
當查詢的索引含有唯一屬性時,將next-key lock降級為record key
Gap鎖設計的目的是為了阻止多個事務將記錄插入到同一範圍內,而這會導致幻讀問題的產生
有兩種方式顯式關閉gap鎖:(除了外來鍵約束和唯一性檢查外,其餘情況僅使用record lock) A. 將事務隔離級別設定為RC B. 將引數innodb_locks_unsafe_for_binlog設定為1