案例描述
在定時腳本運行過程中,發現當備份表格的sql語句與刪除該表部分數據的sql語句同時運行時,mysql會檢測出死鎖,并打印出日志。
兩個sql語句如下:
(1)insert into backup_table select * from source_table
(2)DELETE FROM source_table WHERE Id>5 AND titleWeight<32768 AND joinTime<'$daysago_1week'
teamUser表的表結構如下:
PRIMARY KEY (`uid`,`Id`),
KEY `k_id_titleWeight_score` (`Id`,`titleWeight`,`score`),
ENGINE=InnoDB
兩語句對source_table表的使用情況如下:

死鎖日志打印出的時間點表明,語句(1)運行過程中,當語句(2)開始運行時,發生了死鎖。
當mysql檢測出死鎖時,除了查看mysql的日志,還可以通過show InnoDB STATUS /G語句在mysql客戶端中查看最近一次的死鎖記錄。由于打印出來的語句會很亂,所以,最好先使用pager less命令,通過文件內容瀏覽方式查看結果,會更清晰。(以nopager結束)
得到的死鎖記錄如下:


根據死鎖記錄的結果,可以看出確實是這兩個語句發生了死鎖,且鎖沖突發生在主鍵索引上。那么,為什么兩個sql語句會存在鎖沖突呢?沖突為什么會在主鍵索引上呢?語句(2)得到了主鍵索引鎖,為什么還會再次申請鎖呢?
鎖沖突分析
2.1 innodb的事務與行鎖機制
MySQL的事務支持不是綁定在MySQL服務器本身,而是與存儲引擎相關,MyISAM不支持事務、采用的是表級鎖,而InnoDB支持ACID事務、 行級鎖、并發。MySQL默認的行為是在每條SQL語句執行后執行一個COMMIT語句,從而有效的將每條語句作為一個單獨的事務來處理。
2.2 兩語句加鎖情況
在innodb默認的事務隔離級別下,普通的SELECT是不需要加行鎖的,但LOCK IN SHARE MODE、FOR UPDATE及高串行化級別中的SELECT都要加鎖。有一個例外,此案例中,語句(1)insert into teamUser_20110121 select * from teamUser會對表teamUser_20110121(ENGINE= MyISAM)加表鎖,并對teamUser表所有行的主鍵索引(即聚簇索引)加共享鎖。默認對其使用主鍵索引。
而語句(2)DELETE FROM teamUser WHERE teamId=$teamId AND titleWeight<32768 AND joinTime<'$daysago_1week'為刪除操作,會對選中行的主鍵索引加排他鎖。由于此語句還使用了非聚簇索引KEY `k_teamid_titleWeight_score` (`teamId`,`titleWeight`,`score`)的前綴索引,于是,還會對相關行的此非聚簇索引加排他鎖。
新聞熱點
疑難解答