一個線上項目報的死鎖,簡要說明一下產生原因、處理方案和相關的一些點.
1、背景
這是一個類似數據分析的項目,數據完全通過LOAD DATA語句導入一個InnoDB表中。為方便描述,表結構簡化為如下:
| Create table tb(id int primary key auto_increment, c int not null) engine=innodb; |
導入數據的語句對應為
| Load data infile ‘data1.csv' into table tb;Load data infile ‘data2.csv' into table tb; |
| cat Data1.csv1 1002 1003 100Cat data2.csv10 10011 10012 100 |
產生死鎖的證據是在show engine innodb status的LATEST DETECTED DEADLOCK段中看到死鎖信息,簡化為如下:

說明
從上面表格中看出,事務1在等待某一行的鎖。而事務2持有這行的鎖,但等待表的自增鎖(AUTO_INC),判斷為死鎖,事務回滾。
這里事務1沒有寫出來,但是可以推斷,事務1持有這個表的自增鎖(否則就不是死鎖了)。
2、背景知識1:AUTO_INC lock 及其選項
在InnoDB表中,若存在自增字段,則會維護一個表級別的鎖,這里稱為自增鎖。每次插入新數據,或者update語句修改了此字段,都會需要獲取這個鎖
由于一個事務可能包含多個語句,而并非所有的語句都與自增字段有關,因此InnoDB作了一個特殊的處理,自增鎖在一個語句結束后馬上被釋放。之所以說是特殊處理,是因為普通的鎖,都是在事務結束后釋放。
若一個表有自增字段,一個insert語句不指定該字段的值,或指定為NULL時,InnoDB會給它賦值為當前的AUTO_INCREMENT的值,然后AUTO_INCREMENT加1。
與這個自增鎖相關的一個參數是innodb_autoinc_lock_mode. 默認值為1,可選為0,1,2。
我們先來看當這個值設置為0時,一個有自增字段的表,插入一行數據時的行為:
1) 申請AUTO_INC鎖
2) 得到當前AUTO_INCREMNT值n,給AUTO_INCREMENT 加1
3) 執行插入操作,并將n填入新增的行對應字段中
4) 釋放AUTO_INC鎖
我們看到這個過程中,雖然InnoDB為了減少鎖粒度,在語句執行完成就馬上釋放,但這鎖還是太大了――它包括了插入操作的時間。這就導致了兩個insert語句,實際上沒辦法并行。
沒有這個參數之前,行為就是與設置為0相同,0這個選項就是留著兼容的。
很容易想到設置為1的時候,應該是將3) 和 4)對調。但是本文還是要討論為0的情況,因為我們的前提是LOAD語句,而LOAD語句這類插入多行的語句中(包括insert …select …),即使設置為1也沒用,會退化為0的模式。
3、背景知識2:LOAD DATA語句的主從行為
新聞熱點
疑難解答