1、故事起因于2016年11月15日的一個生產bug。業務場景是:歸檔一個表里邊的數據到歷史表里邊,同是刪除主表記錄。
2、背景場景簡化如下(數據庫引擎InnoDb,數據隔離級別RR[REPEATABLE])
| -- 創建表test1 CREATE TABLE test1 ( id int(11) NOT NULL AUTO_INCREMENT, name varchar(10) NOT NULL, PRIMARY KEY (id) ); insert into test1 values('hello'); -- 創建表test2 CREATE TABLE test2 ( id int(11) NOT NULL AUTO_INCREMENT, name varchar(10) NOT NULL, PRIMARY KEY (id) ); -- Transcation 1 begin; insert into test2 select * from test1 where id = 1; delete from test1 where id = 1; -- Transcation 2 begin; insert into test2 select * from test1 where id = 1; | 
3、具體執行順序
| Transcation1 | Transcation2 | 
| begin; — 這條sql得到test1表主鍵索引鎖共享鎖S(id=1) insert into test2 select * from test1 where id = 1;  |       |
| begin; — 這條sql試圖獲取test1表主鍵索引鎖共享鎖S(id=1),但是已經被T1占有,所以它進入鎖請求隊列. insert into test2 select * from test1 where id = 1;  |     |
| — 這條sql試圖把自己擁有的test1表主鍵索引鎖共享鎖S(id=1)升級為排它鎖X(id=1) — 這時T1也發起一個鎖請求,這個時候mysql發現鎖請求隊列里邊已存在一個事物T2對(id=1)的這條記錄申請了S鎖,死鎖產生了。 delete from test1 where id = 1;  |       |
| 死鎖產生后mysql根據兩個事務的權重,事務2的權重更小,被選為死鎖的犧牲者,rollback。 | |
| T2 rollback 之后T1成功獲取了鎖執行成功 | 
Mysql 官方解釋
Deadlock occurs here because client A needs an X lock to delete the row. However, that lock request cannot be granted because client B already has a request for an X lock and is waiting for client A to release its S lock. Nor can the S lock held by A be upgraded to an X lock because of the prior request by B for an X lock. As a result, InnoDBgenerates an error for one of the clients and releases its locks. The client returns this error。
實際場景和mysql文檔有些區別,文檔里邊要獲取的是X鎖。具體事例里邊要獲取的是S鎖。
下面我們來具體的一步步分析下mysql的死鎖
1、MySQL常用存儲引擎的鎖機制
MyISAM和MEMORY采用表級鎖(table-level locking)
BDB采用頁面鎖(page-level locking)或表級鎖,默認為頁面鎖
InnoDB支持行級鎖(row-level locking)和表級鎖,默認為行級鎖
2、各種鎖特點
表級鎖:開銷小,加鎖快;不會出現死鎖;鎖定粒度大,發生鎖沖突的概率最高,并發度最低
新聞熱點
疑難解答