InnoDB鎖學習

bitifi發表於2016-04-18

Innodb實現標準行級鎖,2種模式的鎖:

l  S (shared locks)         共享鎖:允許擁有鎖的事務讀一行資料

l  X (exclusive locks)     排他鎖:允許有用鎖的事務更新或刪除一行資料

 

如果一個事務T1在行r擁有一個S共享鎖,從不同的事務T2請求鎖定行r,處理如下:

l  事務T2能立即獲得行rS共享鎖,因此,T1T2都在行r上持有S共享鎖

l  事務T2不能獲得行rX排它鎖

如果一個事務T1在行r持有x排它鎖,其他事務T2無法獲得任何型別的鎖。

InnoDB意向鎖

Innodb支援多粒度鎖定,這種鎖定允許事務在行級上的鎖和表級上的鎖同時存在。為了支援在不同粒度上進行加鎖操作,innodb支援一種額外的鎖方式,稱為意向鎖(intention locks)。

Innodb意向鎖為表級別的鎖,設計的主要目的主要是為了在下一個事務中揭示下一行將被請求的鎖型別。

 

InnoDB中有2中型別的意向鎖:

? Intention shared (IS): Transaction T intends to set S locks on individual rows in table t.

                                               事務T想對錶t的某些行設定共享鎖

? Intention exclusive (IX): Transaction T intends to set X locks on those rows.

                                               事務T想對錶t的某些行設定排它鎖

 

例如:SELECT ... LOCK IN SHARE MODE 設定了一個IS

        SELECT ... FOR UPDATE 設定一個IX

 

意向鎖協議如下:

? 一個事務在可以獲得表t的某些行的S共享鎖,必須先獲取表tIS意向共享鎖

? 一個事務在可以會的表t的某些行的X排它鎖,必須先獲取表tIX意向排它鎖

 

Innodb鎖相容性矩陣

clip_image002

意向鎖不會堵塞除了全表操作(例LOCK TABLES ...WRITE以外的任何請求,其意向鎖的主要目的是顯示某些操作鎖定了某行,或者將要鎖定某行。

意向鎖的存在價值在於在定位到特定的行所持有的鎖之前,提供一種更粗粒度的鎖,可以大大節約引擎對於鎖的定位和處理的效能,因為在儲存引擎內部,鎖是由一塊獨立的資料結構維護的,鎖的數量直接決定了記憶體的消耗和併發效能。例如,事務A對錶t的某些行修改(DML通常會產生X鎖),需要對t加上意向排它鎖,在A事務完成之前,B事務來一個全表操作(alter table等),此時直接在表級別的意向排它鎖就能告訴B需要等待(因為t上有意向鎖),而不需要再去行級別判斷

 

意向鎖實際上可以理解為一種暗示未來需要什麼樣行級鎖:

IS表示未來可能需要在這個表的某些記錄上加共享鎖

IX表示未來可能需要在這個表的某些記錄上加排他鎖。

 

意向鎖是表級別的,ISIX鎖之間相互並不衝突,但與表級S/X鎖衝突。

在對記錄加S鎖或者X鎖時,必須保證其在相同的表上有對應的意向鎖或者鎖強度更高的表級鎖

InnoDB記錄鎖、GAP鎖和Next-Key

Innodb的行鎖的型別:

? Record lock: 鎖定一個索引記錄

? Gap lock: 鎖定一個範圍,不包含記錄本身

? Next-key lock: Gap lock+ Record lock,鎖定一個範圍,並且鎖定記錄本身

 

Record Locks總是鎖住索引記錄,即使表沒有定義索引,Innodb會建立一個隱試的聚集索引來鎖定。

 

Next-key Locks 預設Innodb使用RR的隔離級別。在這種情況下,InnoDB使用next-key鎖機制來查詢資料和索引掃描,防止幻象問題

 

Next-key locking結合了index-row鎖和Gap鎖。當搜尋和查詢索引操作時,InnoDB使用行鎖的方式,在相應的索引記錄上設定共享或者排他鎖。因此,行級別的鎖實際上是索引記錄鎖。而Next-Key鎖是索引記錄鎖加範圍鎖。如果一個會話在索引記錄R上加了一個共享或者排他鎖,其他會話不能立即插入一個新的索引記錄在索引記錄R之前的間隙中。

 

假設一個索引的值有10, 11, 13, 20,那麼該索引可能被Next-Key Locking的區間為:

clip_image002

最後一個間隔範圍,Next-Key 實際上鎖定的範圍是僅僅是最大索引值後面的範圍。

 

當操作的索引是唯一的情況下,Innodb會對Next-key Locking進行最佳化,將其降級為Record Lock,只鎖住索引本身,而不是鎖定範圍。

如下:

Session A

Session B

Session A>drop table t;
Session A>create table t (a int primary key);
Session A>insert into t select 1;
Session A>insert into t select 2;
Session A>insert into t select 5;
Session A>begin;
Session A>select * from t where a=5 for update;
+---+
| a |
+---+
| 5 |
+---+
1 row in set (0.00 sec)

 

 

Session A>begin;
Query OK, 0 rows affected (0.00 sec)

Session A>insert into t select 4;
Query OK, 1 row affected (0.00 sec)
Records: 1  Duplicates: 0  Warnings: 0
#
此時並沒有出現等待,能插入並且提交
Session A>commit;
Query OK, 0 rows affected (0.00 sec)

#session A提交上面的事務                          Session A>commit;
Query OK, 0 rows affected (0.00 sec)

 

上面的實驗中,表t1,25三個值。在session A中對a=5進行鎖定讀X鎖定,但因為欄位a是主鍵(唯一索引),因此此時鎖定的僅是5這個索引記錄,而不是(25]這個範圍,這樣在Session B中能順利的插入a=44在(2,5]之間)而不會阻塞。上述正式因為Next-Key Lock對唯一索引的記錄降級為record lock的原因,從而可以提高應用的併發性。

 

當查詢的列是非唯一的索引時,情況如下:

Session A

Session B

Session A>drop table t;
Session A>create table t(id int primary key,v int ,key (v));
Session A>insert into t values (2,20),(6,13),(10,15),(9,15),(3,20),(8,25);
Session A>begin;
Session A>select * from t where v=20 for update;

此時V是非唯一索引,for update鎖定讀會將滿足V=20對應的聚集索引3相應的行加上X行鎖,而索引V則加Next-key Lock,鎖定範圍是[15,20)以及下一個範圍(20,25)

 

注意:右邊的Session B看到15被堵塞,而20沒有被堵塞;所以,推測此處Gap Lock的範圍是【15,20)和(20,25)。這與官當上的http://dev.mysql.com/doc/refman/5.7/en/innodb-record-level-locks.html舉的例子的 可能的間隔範圍剛好閉開區間是反的

#18[15,20)之間,被堵塞
Session B>insert into t select 11,18;
#20記錄上持有輔助索引的X鎖,被堵塞
Session B>insert into t select 11,20;
#21(20,25)之間 被堵塞
Session B>insert into t select 11,21;
#15[15,20)之間,被堵塞
Session B>insert into t select 11,15;
#25插入成功
Session B>insert into t select 11,25;
Query OK, 1 row affected (0.00 sec)                                  
#1426在間隙範圍外都能順利插入成功
Session B>insert into t select 12,14;
Query OK, 1 row affected (0.00 sec)
Session B>insert into t select 13,26;
Query OK, 1 row affected (0.00 sec)

 

Gap Locks 當使用唯一索引查詢唯一的行時,不需要Gap locks.

前面next-key的例子表明gap可能覆蓋一個單獨的索引值,多個索引值,或可能是空的。

 

例如:下面的語句只需要在id100的行上使用一個index-record lock,而不會影響其他會話插入資料:

SELECT * FROM child WHERE id = 100 for update;

如果ID不是索引或者不是一個uniq index,該語句則使用Gap Locking.

 

插入意向Gap鎖(insert intention gap lock)在Insert操作之前被設定。這種鎖的目的是:當多個事務插入資料到相同的索引間隙,只要他們插入的索引間隙內的位置不同,則不需要相互等待。假如有索引值47,某個事務檢視在47的間隙間插入56,在插入的行會分配插入意向Gap鎖,但因為56的行並不衝突,所以不會堵塞對方。

 

注意:不同的事務在一個間隙(Gap)上可以持有互斥的鎖。例如:當事務B在某個Gap上擁有一個排他Gap,事務A在相同的Gap上可以持有共享Gap鎖。此處允許鎖衝突共存的原因是:當從一個索引上purge一個記錄,其他事務對此記錄加的Gap鎖會被被合併。

 

Gap鎖在Innodb中是完全禁止的。這意味著它們只能防止其他事務在一個Gap中的插入操作。因此,排他Gap鎖和共享Gap鎖具有同樣的效果。

 

Disabling Gap Locking

Gap鎖可以被完全關閉 方法是設定事務隔離級別修改為RC,或者開啟innodb_locks_unsafe_for_binlog系統變數(此選項5.65.7已經被棄用)。關閉Gap的情況下,除了在外來鍵一致性檢查和逐漸衝突檢查的時候,gap鎖均不生效。

 

RC隔離級別和開啟innodb_locks_unsafe_for_binlog引數也會帶來其他影響,比如:mysql分析where條件之後,未匹配行的記錄鎖就會被釋放(違反了2PL原則)例如:在update中,innodb執行"半一致性"讀,這樣,最新的提交版本被告知mysql,然後由mysql決定行是否匹配執行Update操作的where條件。


參考連結:

http://dev.mysql.com/doc/refman/5.7/en/innodb-lock-modes.html

http://dev.mysql.com/doc/refman/5.7/en/innodb-record-level-locks.html

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/30496894/viewspace-2083351/,如需轉載,請註明出處,否則將追究法律責任。

相關文章