1. 鎖分類
innodb中的鎖分為S鎖,即共享鎖,另一種為X鎖,排它鎖,比如:
共享鎖(S)
select * from supplier where id=5 lock in share mode;
複製程式碼
排他鎖(X)
select * from supplier where id=5 for update;
複製程式碼
或者insert,delete,update語句,這都是排他鎖
相容性
這兩種鎖的相容如下:
X | S | |
---|---|---|
X | N | N |
S | N | Y |
意向鎖
可以理解為屬於S鎖和X鎖的父節點,即要獲取S鎖或者X鎖的話,必須先提前獲取意向鎖,即IS或者IX鎖,那綜合起來看下,這兩類鎖的相容情況如下
X | S | IX | IS | |
---|---|---|---|---|
X | N | N | N | N |
S | N | Y | N | Y |
IX | N | N | Y | Y |
IS | N | Y | Y | Y |
隱式鎖
也是醉了,為什麼要搞這麼多概念。。這種鎖,可以認為不衝突的時候不加鎖,這個時候的鎖就是隱式鎖,等遇到衝突了,該鎖會升級為顯示鎖,還是通過一個例子來說明吧:
事務1(其中age是普通索引)select * from test01 where age=21 for update;
複製程式碼
我們看到mysql中其實是沒有找到鎖的:
事務2
insert into test01(id,name,age) values(8,'zzh',22);
複製程式碼
這個時候由於[21,23)有gap鎖,所以事務2會被阻塞住,這個時候再看mysql中的鎖記錄
可以看到一開始事務1雖然使用的是...for update,但是由於沒有衝突所以加的是隱式鎖,等到事務2開始導致有衝突存在,所以事務1的鎖改為現實鎖。2.加鎖分析與實踐
具體在事務的不同隔離級別下,不同的場景加鎖分析,在hedecheng加鎖分析這篇文章已經講解的非常詳細了,這裡就不再多說了。主要說下其他情況下總結的一些加鎖分析。
2.1 插入意向鎖(Insert Intention Locks)
先來看官網對該鎖的解釋
我們重點關注下紅色的區域:在遇到衝突的時候,會在該索引所在的位置加S鎖,而該共享鎖又很容易導致死鎖,官網中就有個例子來專門說明這種共享鎖導致的死鎖,即三個事務同事執行一條插入語句其中一個事務回滾導致死鎖: 我們可以簡單的把這種情況模仿一遍: 事務1 insert into test01(id,name,age)values(12,"zzh",33);
複製程式碼
事務2
insert into test01(id,name,age)values(12,"zzh",33);
複製程式碼
事務3
insert into test01(id,name,age)values(12,"zzh",33);
複製程式碼
如下圖操作:
我們看下Mysql中鎖的記錄 其中這個就印證了在意向鎖衝突的時候,請求加的是S鎖,然後我們回滾事務1事務1
rollback
複製程式碼
這個時候我們看到事務2成功了,事務3出現死鎖
我們可以從死鎖日誌中看到:
- 事務2:等待鎖模式為X的插入意向鎖
- 事務3:等待鎖模式為X的插入意向鎖;擁有鎖模式為S的record lock
- 暗含條件:事務2也擁有鎖模式為S的record lock,這才導致了死鎖,死鎖日誌會把最終成功獲取鎖了的事務已經擁有的鎖不會列印出來
所以存在 事務2->事務3,事務3->事務2 死鎖發生
官網中還要另一個類似的例子,這裡就不多做分析了,原因類似。
下面我們看下另一種情況:
事務1(事務id=2944)select * from test01 where age=21 for update;
複製程式碼
事務2(事務id=2945)
insert into test01(id,name,age)values(2,"zzh",22);
複製程式碼
事務3(事務id=2946)
select * from test01 where age=21 lock in share mode;
複製程式碼
執行如下
mysql中鎖記錄 我們可以看到- 事務1:在primary key的(X,RECORD LOCK),在age_idx的(X,RECORD LOCK)
- 事務2:在primay key 的(S,RECORD LOCK),這個就是我們前面講到的,事務2在獲取插入意向鎖出現衝突,所以阻塞在了要獲取(S,RECORD LOCK)
- 事務3:在age_idx的(S,RECORD LOCK),
然後我們提交事務1 事務1
commit;
複製程式碼
- 事務2:加S鎖成功,但是出現唯一鍵衝突,直接報錯,這裡有個很重要的一點是,在事務對該行成功加S鎖之後發現記錄存在的情況下,會直接報錯,但是雖然報錯,S鎖不會釋放
- 事務3:獲取S鎖成功,但是是在age_idx上,跟事務2不一樣。
我們再重新執行事務1
事務1(事務id=29467)
select * from test01 where age=21 for update;
複製程式碼
執行結果被阻塞:
我們看下mysql中的鎖記錄: 可以看到事務3擁有在age_idx的S鎖,阻塞了事務1要獲取的X鎖,那這個時候我們提交事務3 事務3 commit; 我們發現事務1還處於阻塞的狀態,看下mysql中的鎖記錄 這個時候明白了,事務2雖然執行失敗,但是其由於插入意向鎖衝突所加的S鎖並未釋放,所以會導致事務1還處於阻塞中。只有當我們提交了事務2,事務1才會真正執行。2.2 select...for update
該語句加鎖分為兩種情況
- 記錄存在:如果是RC模式下,加的是(X,RECORD LOCK),RR 模式下,加的是(X,NEXT-KEY LOCK),該鎖相互不相容
- 記錄不存在:加(X,GAP)鎖,並且該鎖是相容的,但是與Insert Intention Locks 不相容,這個很容易導致死鎖
2.2.1 不同索引之間等待出現死鎖
事務1select * from test01 where age=21 for update;
複製程式碼
事務2
insert into test01(id,name,age)values(3,"zzh",22);
複製程式碼
事務1
insert into test01(id,name,age)values(3,"zzh",100);
複製程式碼
按如上順序執行結果:
我們發現出現死鎖,事務2被mysql回滾之後事務1執行成功。我們看下具體的死鎖日誌 通過死鎖日誌我們可以分析出來- 事務1: 擁有age_idx(X,NEXT-KEY LOCK),加插入意向鎖存在衝突,所以等待id=3位置的(S,RECORD LOCK)
- 事務2: 等待age_idx的插入意向鎖(插入意向鎖不只是在primary key上才有)
- 暗含條件:事務2雖然在age_idx上等待插入意向鎖,但是在id=3位置上加插入意向鎖成功了,這才有了事務1在該位置加插入意向鎖存在衝突
2.2.2 記錄不存在導致的死鎖
事務1(事務id=29684)select * from test01 where age=21 for update;
複製程式碼
事務2(事務id=29683)
select * from test01 where age=21 for update;
複製程式碼
事務1
insert into test01(id,name,age)values(3,"zzh",22);
複製程式碼
執行結果如下
可以看到- 事務1和事務2同時持有了age=22這個記錄的gap鎖,由於記錄不存在,所以此時的gap鎖相容
- 但是記錄不存在相容的gap鎖和插入意向鎖兵不相容,所以事務1向age=22索引所請求的插入意向鎖會等待。
事務2
insert into test01(id,name,age)values(3,"zzh",22);
複製程式碼
由上面的分析同樣得出事務2的等待有兩種情況:
(1) 和事務1一樣,等待age=22的插入意向鎖,此時發現已經有事務1在等待該位置的插入意向鎖,那就等待該位置的S鎖
(2) 事務1雖然在age=22的插入意向鎖等待,但是id=3的插入意向鎖是加成功了,所以如果事務2在id=3這裡等待插入意向鎖的話,也會有衝突,那就等待該位置的S鎖
所以,有這兩種情況都會導致事務出現死鎖,我們具體看下死鎖日誌:
我們看到:- 事務1在等待age=22位置的插入意向鎖
- 事務2在等待 id=3位置的S鎖,也就是我們分析的第二種情況,同時事務2擁有age=22位置的gap鎖
我們上面有分析,事務2的等待應該是有兩種情況,而出現這兩種情況的可能就是事務2的這條語句
insert into test01(id,name,age)values(3,"zzh",22);
複製程式碼
我們瞭解到在記錄不存在的時候,如果對此記錄進行select...for update語句,該語句會對空位置加gap鎖,這個會比較危險,如果我們的表用的是自增主鍵,而此時如果查詢一個不存在的記錄,那會把未來要插入的所有的空隙都加了gap鎖,會導致以後表中無法在插入任何資料,這個極其危險。
3. 小結
本篇,我們介紹了mysql中的鎖以及在實踐中可能遇到的一些死鎖,專門通過幾個demo集中分析了一下,對於加鎖的分析,在文中所提到的hedecheng的文章中,已經有很深的講解,所以本文並沒有對此總結。重點還是通過一些實際中用到的例子進行實際的分析一下整個過程。重點是插入意向鎖和select...for update中的一些加鎖的分析。下一篇來專門介紹下mysql的MVCC機制