兩個小工具,MySQL死鎖分析,新技能又Get!!!

數據庫死鎖,是最難調試與追蹤的。shell

場景以下:
兩個小工具,MySQL死鎖分析,新技能又Get!!!
同一個表,事務內先插入一條記錄,再更新這條記錄,併發時會死鎖。
兩個小工具,MySQL死鎖分析,新技能又Get!!!數據庫

而且可以復現。session

能夠經過什麼工具模擬併發事務,查看信息,解決問題呢?這是今天要分享的內容。架構

1、前置準備

set session transaction isolation level repeatable read;
set session autocommit=0;

create table t (
id int(20) primary key AUTO_INCREMENT,
cell varchar(20) unique
)engine=innodb;

start transaction;
insert into t(cell) values(11111111111);
insert into t(cell) values(22222222222);
insert into t(cell) values(33333333333);
commit;

說明:
(1)案發時,事務隔離級別RR;
(2)多終端實驗,須要關閉事務自動提交;
(3)建表,設置PK與unique,初始化數據;併發

2、併發事務模擬

Session A:
start transaction;
insert into t(cell)values(44444444444);  [1]
        Session B:
        start transaction;
        insert into t(cell) values(55555555555); [2]
update t set cell=123 where cell=44444444444; [3]   
        update t set cell=456 where cell=55555555555; [4]

開啓兩個終端模擬併發事務:
(1)紅色SQL爲事務A;
(2)黑色SQL爲事務B;
(3)[1][2][3][4]爲執行時序;ide

3、實驗現象

insert into t(cell)values(44444444444); [1]
事務A插入數據,最早執行
結果:插入成功工具

insert into t(cell) values(55555555555); [2]
事務B插入數據,第二執行
結果:插入成果this

update t set cell=123 where cell=44444444444; [3]
事務A修改[1]中插入的數據,第三執行
結果:阻塞,等待執行結果
兩個小工具,MySQL死鎖分析,新技能又Get!!!
畫外音:修改一條本身插入的數據,在等待什麼呢?3d

update t set cell=456 where cell=55555555555; [4]
事務B修改[2]中插入的數據,最後執行
結果:
(1)事務B死鎖,事務B被回滾;
兩個小工具,MySQL死鎖分析,新技能又Get!!!
(2)事務A中,[3]語句阻塞結束,執行成功;
兩個小工具,MySQL死鎖分析,新技能又Get!!!
畫外音:說明事務A中阻塞的語句,確實在等事務B中的某個鎖。調試

4、結果分析

兩個事務,各自修改本身插入的數據,卻產生了死鎖,確實詭異。

上述實驗現象的兩個核心問題是:
(1)語句[3]阻塞,在等待什麼鎖?
(2)語句[4]死鎖,此時事務A和事務B必定是彼此佔住一把鎖,請求彼此的鎖,這些鎖又是什麼呢?

工具一:

show engine innodb status;
畫外音:前文《超讚,InnoDB調試死鎖的方法!》就詳細分享過,InnoDB死鎖的分析實踐。

執行以後,顯示的內容以下(放大仔細看):
兩個小工具,MySQL死鎖分析,新技能又Get!!!
信息不少,別急,樓主娓娓道來。

第一部分,關鍵詞是:
(1)Transaction 1,事務3998;
(2)在執行
update t set cell=123 where cell=44444444444;
(3)正在等待鎖釋放(waiting for this lock to be granted),記錄鎖(record locks),主鍵索引上(index primary),互斥鎖(lock_mode X),物理記錄(physical record),asc 55555555555;
畫外音:英文比較差沒事,抓關鍵詞。

畫外音,InnoDB存儲引擎,彙集索引與非彙集索引的實現方式,決定了鎖會加在彙集索引上,詳見文章:
《1分鐘瞭解MyISAM與InnoDB的索引差別》。

第二部分,關鍵詞是:
(1)Transaction 2,事務3999;
(2)正在執行
update t set cell=456 where cell=55555555555;
(3)持有鎖(holds the lock),記錄鎖(record locks),主鍵索引上(index primary),互斥鎖(lock_mode X),物理記錄(physical record),asc 55555555555;
(4)正在等待鎖釋放(waiting for this lock to be granted),記錄鎖(record locks),主鍵索引上(index primary),互斥鎖(lock_mode X),物理記錄(physical record),asc 11111111111;
(5)事務2回滾(we roll back transaction 2);

經過show engine innodb status; 可以看到不少事務與鎖之間的信息,對分析問題十分有幫助,這些信息,可以解釋一些問題,但仍有兩個疑惑:
(1)事務1爲啥想拿55555555555的鎖?
畫外音:這正是,事務1被阻塞的緣由。

(2)事務2爲啥想拿11111111111的鎖?死鎖的發生,說明事務1此時真佔着11111111111的鎖,這又是爲何呢?
畫外音:第一個事務佔111搶555,第二個事務佔555搶111,循環嵌套,纔會死鎖。

工具二:

explain
爲了進一步尋找緣由,能夠經過explain看下致使死鎖語句的執行計劃。

explain update t set cell=456 where cell=55555555555;
兩個小工具,MySQL死鎖分析,新技能又Get!!!

select_type:SIMPLE
這是一個簡單類型的SQL語句,不含子查詢或者UNION。

type:index
訪問類型,即找到所需數據使用的遍歷方式,潛在的方式有:
(1)ALL(Full Table Scan):全表掃描;
(2)index:走索引的全表掃描;
(3)range:命中where子句的範圍索引掃描;
(4)ref/eq_ref:非惟一索引/惟一索引單值掃描;
(5)const/system:常量掃描;
(6)NULL:不用訪問表;

上述掃描方式,ALL最慢,逐步變快,NULL最快。

懷疑點1:明明cell字段有uniq索引,爲什麼要進行走PK索引的全表掃描呢?

possible_keys:NULL
可能在哪一個索引找到記錄。

key:PRIMARY
實際使用索引。
畫外音:使用PK進行的全表掃描。

ref:NULL
哪些列,或者常量用於查找索引上的值。

懷疑點2:where條件中的查詢條件55555555555,原本應該做爲在索引上被檢索的值呀?

rows:5
找到所需記錄,預估須要讀取的行數。

懷疑點3:明明修改的是5,爲什麼初始化的1,2,3,以及第一個事務插入的4,以及第二個事務插入的5,都要被讀取呢?不該該全表掃描呀。

經過explain,基本已經能夠判斷:
update t set cell=456 where cell=55555555555;
並無和咱們預想同樣,走cell索引進行查詢,而是走了PK索引進行了全表掃描。

再仔細一看:

create table t (
id int(20) primary key AUTO_INCREMENT,
cell varchar(20) unique
)engine=innodb;

建表的時候cell定義的是字符串類型。

而更新的時候,
update t set cell=456 where cell=55555555555;
使用的是整數類型。

類型轉換,會致使全表掃描,出現鎖升級,鎖住所有記錄。

加上引號,再次經過explain驗證一下:
explain update t set cell= '456 ' where cell= '55555555555 ';
兩個小工具,MySQL死鎖分析,新技能又Get!!!
果真印證了猜測:
(1)type:range,變爲了走索引的字符串比對,範圍掃描;
(2)possible_keys:cell,經過cell索引找到了記錄;
(3)key:cell,實際使用cell索引;
(4)ref:const,使用了常量' 555'進行比對;
(5)rows:1,預估讀取行數是1;

這下所有能夠解釋了。
兩個小工具,MySQL死鎖分析,新技能又Get!!!

總結

就本例而言:須要注意字符串與整數之間的強制類型轉換,有時候少一個引號,就會使得行鎖升級爲表鎖。

死鎖是MySQL中很是難調試的問題,常見的思路與方法有:
(1)經過多終端模擬併發事務,復現死鎖;
(2)經過show engine innodb status; 能夠查看事務與鎖的信息;
(3)經過explain能夠查看執行計劃;

思路比結論更重要,但願你們有收穫。
兩個小工具,MySQL死鎖分析,新技能又Get!!!
架構師之路-分享技術思路
相關推薦:
《MySQL併發控制與鎖+調試MySQL死鎖d方法》
《寫一個cache,要掌握哪些技術點》
《6條shell小技巧 | 1分鐘系列》
《MyISAM與InnoDB的索引差別 | 1分鐘系列》
《超讚,InnoDB調試死鎖的方法!》

末了,有不少同窗經過經驗,分析出了是強制類型轉換的緣由,贊!畫外音:但本文「思路」仍值得轉,不是嗎?

相關文章
相關標籤/搜索