

784·396
文章目錄
- InnoDB 行鎖的3種算法
- 死鎖
- 意向鎖
InnoDB 行鎖的3種算法
InnoDB存儲引擎有3種行鎖的算法,其分别是:
❑Record Lock:單個行記錄上的鎖
❑Gap Lock:間隙鎖,鎖定一個範圍,但不包含記錄本身
❑Next-Key Lock∶Gap Lock+Record Lock,鎖定一個範圍,并且鎖定記錄本身
Record Lock總是會去鎖住索引記錄,如果InnoDB存儲引擎表在建立的時候沒有設定任何一個索引,那麼這時 InnoDB存儲引擎會使用隐式的主鍵來進行鎖定。 Next-Key Lock是結合了Gap Lock和Record Lock的一種鎖定算法,在Next-Key Lock算法下,InnoDB對于行的查詢都是采用這種鎖定算法。
在InnoDB 預設的事務隔離級别下,即REPEATABLE READ下,InnoDB存儲引擎采用Next-Key Locking這種鎖定 算法。例如一個索引有10,11,13和20這四個值,那麼該索引可能被Next-Key Locking的區間為: (-∞,10] (10,11] (11,13] (13,20] (20,+∞)
當查詢的索引含有唯一屬性時,InnoDB存儲引擎會對Next-Key Lock進行優化,将其降級為 Record Lock,即僅鎖住索引本身,而不是範圍。 什麼是唯一屬性,其實就是我們所說的能夠辨別該行資料唯一的辨別。 unique 字段。比如:主鍵就是唯一的,不重複的。我們也可以自己設計多個字段組合不重 複,唯一的。
DROP TABLE IF EXISTS t;
CREATE TABLE t(a INT PRIMARY KEY);
INSERT INTO t SELECT 1;
INSERT INTO t SELECT 2;
INSERT INTO t SELECT 5;
表t共有1、2、5三個值。在上面的例子中,在會話A中首先對a=5進行X鎖定。而由于a是主 鍵且唯一,是以鎖定的僅是5這個值,而不是(2,5)這個範圍,這樣在會話B中插入值4而不 會阻塞,可以立即插入并傳回。即鎖定由Next-Key Lock算法降級為了Record Lock,進而提高應用的并發性。
CREATE TABLE z(a INT,b INT, PRIMARY KEY(a),KEY(b));
INSERT INTO z SELECT 1,1;
INSERT INTO z SELECT 3,1;
INSERT INTO z SELECT 5,3;
INSERT INTO z SELECT 7,6;
INSERT INTO z SELECT 10,8;
表z的列b是輔助索引,若在會話A中執行下面的SQL語句:
SELECT * FROM z WHERE b=3 FOR UPDATE
很明顯,這時SQL語句通過索引列b進行查詢,該列不是唯一屬性,是以其使用傳統的Next-Key Locking技術加鎖,并且由于有兩個索引,其需要分别進行鎖定。對于聚集索引(primay-key a),其僅對列a等于5的索引加 上Record Lock。而對于輔助索引b,其加上的是Next- Key Lock,鎖定的範圍是(1,3)。特别需要注意的是, InnoDB存儲引擎還會對輔助索引下一個鍵值加上gap lock,即還有一個輔助索引範圍為(3,6)的鎖。
SELECT * FROM z WHERE a = 5 LOCK IN SHARE MODE;
INSERT INTO z SELECT 4,2;
INSERT INTO Z SELECT 6,5;
第一個SQL語句不能執行,因為在會話A中執行的 SQL語句已經對聚集索引中列a=5的值加上X鎖,因 此執行會被阻塞。
第二個SQL語句,主鍵插入4,沒 有問題,但是插入的輔助索引值2在鎖定的範圍(1, 3)中,是以執行同樣會被阻塞。第三個SQL語句, 插入的主鍵6沒有被鎖定,5也不在範圍(1,3)之間。 但插入的值5在另一個鎖定的範圍(3,6)中,故同樣 需要等待。
InnoDB 行鎖 next-key lock 有什麼作用
在預設的事務隔離級别下,即REPEATABLE READ下,InnoDB存儲引擎采用Next-Key Locking機制來避免 Phantom Problem(幻讀問題,也稱不可重複讀)。Phantom Problem是指在同一事務下,連續執行兩次同 樣的SQL語句可能導緻不同的結果。(在 READ COMMITTED 事務隔離級别下會出現)
CREATE TABLE z(a INT,b INT, PRIMARY KEY(a),KEY(b));
INSERT INTO z SELECT 1,1;
INSERT INTO z SELECT 3,1;
INSERT INTO z SELECT 5,3;
INSERT INTO z SELECT 7,6;
INSERT INTO z SELECT 10,8;
在同一事務中,若此時執行語句: SELECT*FROM z WHERE b=3 FOR UPDATE 兩次,中間間隔10秒時間執行。可以肯 定的說,我們會得到第三行資料的結果,即 (5,3)。此時我們知道,會有一個 Record Lock鎖定主鍵 5,還會有一個 gap lock鎖定 (1,3)和 (3,6)。
假設:我們分析下,若此時沒有gap lock(1,3)和 (3,6),如果隻有Record Lock鎖定主鍵 5 會不會造成幻讀。 分析:我們在第一次 select 完成之後,第二次select 之前,插入一天資料: INSERT INTO z SELECT 20,3; 這條資料是可 以插入成功的,因為我們隻有一個record lock 鎖定了 主鍵5,對于新插入的資料主鍵為 20 ,可以插入,且 無重複。 插入完成後,第二次 select 得到了兩個值,(5,3)(20,3)。這就造成了同一事物中,第一次讀取和第二次 讀取的結果不一樣,出現幻讀。 如果有gap lock,插入就會被阻塞,不會出現幻讀。
死鎖
如何預防資料庫死鎖,生産環境如何避免死鎖 MySQL 篇 死鎖是指兩個或兩個以上的事務在執行過程中,因争奪鎖資源而造成的一種互相等待的現象。若無外力作用,事 務都将無法推進下去。解決死鎖問題最簡單的方式是不要有等待,将任何的等待都轉化為復原,并且事務重新開 始。毫無疑問,這的确可以避免死鎖問題的産生。然而線上上環境中,這可能導緻并發性能的下降,甚至任何一 個事務都不能進行。而這所帶來的問題遠比死鎖問題更為嚴重,因為這很難被發現并且浪費資源。 解決死鎖問題最簡單的一種方法是逾時,即當兩個事務互相等待時,當一個等待時間超過設定的某一門檻值時,其 中一個事務進行復原,另一個等待的事務就能繼續進行。在InnoDB存儲引擎中,參數innodb_lock_wait_timeout用 來設定逾時的時間。 逾時機制雖然簡單,但是其僅通過逾時後對事務進行復原的方式來處理,或者說其是根據FIFO的順序選擇復原對 象。但若逾時的事務所占權重比較大,如事務操作更新了很多行,占用了較多的undo log,這時采用FIFO的方式, 就顯得不合适了,因為復原這個事務的時間相對另一個事務所占用的時間可能會很多。 是以,除了逾時機制,目前資料庫還都普遍采用wait-for graph(等待圖)的方式來進行死鎖檢測。較之逾時的解 決方案,這是一種更為主動的死鎖檢測方式。InnoDB存儲引擎也采用的這種方式。wait-for graph要求資料庫儲存以下兩種資訊: 鎖的資訊連結清單和事務等待連結清單。
事務T1指向T2邊的定義為:
❑事務T1等待事務T2所占用的資源
❑事務T1最終等待T2所占用的資源,也就是事務之間在等待相同的資源,而事務T1發生在事務T2的後面
wait-for graph是一種較為主動的死鎖檢測機制,在每個事務請求鎖并發生等待時都會判斷是否存在回路,若存在則有死鎖,通常來說InnoDB存儲引擎選擇復原undo量最小的事務。
意向鎖
InnoDB存儲引擎支援多粒度(granular)鎖定,這種鎖定允許事務在行級上的鎖和表級上的鎖同時存在。 為了支援在不同粒度上進行加鎖操作,InnoDB存儲引擎支援一種額外的鎖方式,稱之為意向鎖(Intention Lock)。 意向鎖是将鎖定的對象分為多個層次,意向鎖意味着事務希望在更細粒度(fine granularity)上進行加鎖 InnoDB存儲引擎支援意向鎖設計比較簡練,其意向鎖即為表級别的鎖。設計目的主要是為了在一個事務中揭示下 一行将被請求的鎖類型。其支援兩種意向鎖:
1)意向共享鎖(IS Lock),事務想要獲得一張表中某幾行的共享鎖
2)意向排他鎖(IX Lock),事務想要獲得一張表中某幾行的排他鎖
由于InnoDB存儲引擎支援的是行級别的鎖,是以意向鎖其實不會阻塞除全表掃以外的任何請求。