在學習mysql鎖過程中有些想法記錄與分享
環境:mysql5.6 innodb存儲引擎,默認隔離級別repeatable-read,可重復讀。innodb_locks_unsafe_for_binlog, 參數默認值是OFF,也就是啟用間隙鎖, 他是一個bool值, 當值為true時表示disable間隙鎖
知識:與oracle不同的是,mysql加鎖是對索引加鎖
在進行刪除或者修改操作時,如果過濾條件列是非唯一索引,為了保證當前讀的數據一致性,mysql通過間隙鎖對數據之間區域進行鎖定。(實際上是通過鎖定索引達到效果)
這種鎖叫間隙鎖,這種鎖定會造成許多誤殺,很多並不沖突的數據會因為間隙鎖而無法插入。
舉例:
准備測試數據
create table test (id int,name varchar(10));
alter table test add primary key PK_ID (id);
alter table test add index INX_Name (name);
insert into test values (1,'a1');
insert into test values (2,'a1');
insert into test values (3,'a1');
insert into test values (4,'a3');
insert into test values (5,'a4');
insert into test values (11,'a6');
insert into test values (21,'a10');
insert into test values (22,'a13');
insert into test values (23,'a14');
insert into test values (24,'a15');
session one;
刪除name='a3',因為間隙鎖的緣故,會對 (24,a15)和(4,a3)之間加鎖,(4,a3)和(5,a4)之間加鎖。
很明顯,如果我們想插入a2到name字段中,必然會因為間隙鎖,而處於等待狀態。
測試:session two
insert into test(id,name) values (30,'a2'); 插入等待,並且鎖等待超時。
同樣a3,a4之間插入數據也會處於等待超時。
這時很容易想到一個問題,如果間隙鎖是在兩組數據中間加鎖,那么如果我插入a3兩邊的數據,a15或者a4,是否會出現鎖等待呢。
繼續做測試:
繼續插入:insert into test(id,name) values(20,'a15');
insert into test(id,name) values(26,'a15');
可以看到插入ID 20很快成功,插入ID 26 出現鎖等待。
同樣是a15,因為ID不同,結果不一樣,是什么原因呢。
我們來看插入后的一個查詢,很容易就理解了:
這個查詢是按照name列的索引做的順序排序,我們可以看到 (20,a15)已經插入成功。
我們已經知道在(24,a15)和(4,a3)之間存在間隙鎖,如果數據(26,a15)需要插入進去,那么必然會排在24 a15之后
形成:
20 a15
24 a15
26 a15
4 a3
而這樣的數據必然會被間隙鎖阻塞,因此導致鎖等待。
所以我們在上面明確間隙鎖的時候,需要書寫一組數據(主鍵列,輔助索引列),而不是單單只寫一列。
最后得出結論:間隙鎖鎖定了輔助索引兩個葉子節點之間的內容,會造成鎖定多余區域的數據。
先delete再insert的操作時常會導致死鎖情況的出現。
應用程序sql中,在做刪除或者更新操作中,對於過濾條件盡量選擇主鍵列或者唯一索引列