1-2 【mysql系列】, 對mysql的innoDB加鎖分析

innoDB的事務,是基於鎖來實現的,用到事務不天然就會用到鎖,而若是對鎖理解的不通透,很容易形成線上問題。數據庫

數據庫加鎖的分析,和事務的引擎,隔離級別,索引,主鍵索引都有關係,spa

若是去考慮引擎和各類隔離級別的話,就會很複雜了,因此下面都是基於innoDB和RR的隔離級別進行分析:.net

 

表結構:rest

內容:orm

 

 

1 , 根據主鍵更新

 

若是根據主鍵來行數blog

 

事務A索引

事務B事務

 

update user set name='ce1' where id='1';get

update user set name='ce3' where Id='3';it

同時執行,都成功

update user set name='ce1' where id='1';

update user set name='ce3' where userId='10003';

B更新失敗,直至:Lock wait timeout

 

結論,若是根據非主鍵來更新,會把整個表進行鎖定,沒法 進行更新操做。

 

注:只要是根據主鍵索引來更新,哪怕事務A沒命中主鍵,也不會鎖定整個表

 

 

2,根據非索引非主鍵更新

 

事務A

事務B

 

update user set name='ce1' where userId='10001';

update user set name='ce3' where Id='3';

或者

update user set name='ce3' where userId='10003'

都會失敗,若是非索引,直接鎖表

 

3, 若是在userId 列上加入普通惟一索引

 

修改爲

再更新

事務A

事務B

 

update user set name='ce1' where userId='10001';

update user set name='ce3' where Id='3';

或者

update user set name='ce3' where userId='10003'

都會成功,若是有惟一索引,也是能成功行數,互相不影響

 

4, 若是在userId 列上加入普通非惟一索引 (重點探討)

把userId改爲非惟一索引:

 

記錄內容以下:

+----+--------+------+

| id | userId | name |

+----+--------+------+

| 1 | 10001 | ce1 |

| 2 | 10002 | ce2 |

| 3 | 10001 | ce3 |

| 4 | 10004 | ce4 |

+----+--------+------+

再相同操做

事務A

事務B

 

update user set name='ce1' where userId='10001';

update user set name='ce3' where Id='3';

B失誤執行失敗,顯然id=3的這行也被鎖住了

其實最終仍是按主鍵鎖住的記錄 id=1和id=3的記錄

非惟一索引與普通索引,更一步的區別是GAP鎖

gap鎖是用於解決幻讀的存在,演示

把記錄修改爲,如:

id爲pk. userId爲Normal key

A事務

B事務

結果

begin;

 

update user set name='ce22' where userId='100020';

   
 

insert into user (userId,name) values('100021','tttt');

直至事務失敗超時

1, 首先GAP鎖針對的是insert操做

2, 當更新userId='100020'時,會鎖住兩邊的記錄區間,防止幻讀的存在。

3, 鎖是做用在普通索引上,但因爲索引是由B+樹存儲,那麼鎖住的是兩邊的區間,防止insert

GAP鎖爲何不是鎖住一條記錄,而是鎖住一個區間呢? 

附上疑問: https://www.oschina.net/question/867417_2289606

其實:

GAP鎖是解決幻讀存在的,如當 delete時就必須鎖住區間了

A事務

B事務

 

begin;

 

delete from user where userId='888888';

   
 

insert into user (userId,name) values('100021','tttt');

OK, 能夠插入

 

insert into user (userId,name) values('100041','tttt');

插入超時

可見,這個GAP鎖,鎖住的是100040~無窮大 的記錄

死鎖的產生分析

1, 兩條語句產生的死鎖

id = pk, userId= key

最簡單的。兩條語句互相更新等待

 

begin;

 

update user set name='ce1' where userId='100010';

begin;

 

update user set name='ce2' where userId='100020';

update user set name='ce2' where userId='100020';

update user set name='ce1' where userId='100010';

 

最簡單的死鎖

 

2, 因爲gap鎖,刪除一臺不存在的記錄

 

如,先刪除一條記錄,而後插入一條記錄, 若是記錄GAP鎖衝突,兩個事務容易互爲死鎖。如:

 

A事務

B事務

begin;

delete from user where userId='100020';

(Query OK, 1 row affected)

begin;

 

 

delete from user where userId='565656';

insert into user (userId,name) values('100041','tttt');

 

 

insert into user (userId,name) values('100019','tttt');

 

結果直接拋出:

 

ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction

 

 

分析:

 

A事務 delete 加入gap鎖【100010,100020】, 第二段【100020,100030】

B事務 delete加入gap 鎖【100040,無窮大】

而後A事務插入,獲取插入意向鎖時B事務的GAP鎖被阻塞

B事務插入,獲取插入意向鎖時時被A事務的GAP鎖阻塞

結果死鎖

 

 

公衆號:

何錦彬 2018.11.21

相關文章
相關標籤/搜索