一次詭異的數據庫“死鎖”,問題究竟在哪里?
程序死鎖的問題,很難調試,看進程堆棧,看各個線程與鎖的情況,對照代碼進行排查。
數據庫死鎖的問題,更難,看不了數據庫堆棧,也看不了數據庫線程與鎖,更難以對照代碼排查。
前段時間,和一個朋友討論了一個“疑似”數據庫死鎖的問題,最后進行試驗與排查,找到了問題所在。
場景如下:
同一個表,高并發事務,事務內先插入一條記錄,再更新這條記錄:
- 如果更新的是唯一索引,有異常;
- 如果更新的是自增主鍵,就沒有異常;
畫外音:先不要被“dead lock”描述所迷惑,是死鎖問題,阻塞問題,還是其他異常,還另說。
而且,據朋友所述,還能夠復現:
- 開啟事務;
- 插入記錄;
- sleep 5秒;
- 修改被插入的記錄;
在并發時穩定復現。
根據朋友的描述,在線下開了多個MySQL客戶端進行了并發模式測試,結果還挺出乎意料的。
第一步:數據準備
- create table t (
- id int(20) primary key AUTO_INCREMENT,
- cell varchar(20) unique
- )engine=innodb;
新建表:
- 存儲引擎是innodb,MySQL版本是5.6;
- id字段,自增主鍵;
- cell字段,唯一索引;
- start transaction;
- insert into t(cell) values(11111111111);
- insert into t(cell) values(22222222222);
- insert into t(cell) values(33333333333);
- commit;
插入一些測試數據。
第二步:session參數設置
事務的隔離級別,事務的自動提交等參數設置不當,都會對實驗的結果產生影響,詢問了朋友,事務的隔離級別是RR(repeatable read)。
- set session autocommit=0;
- set session transaction isolation level repeatable read;
每一個session啟動后:
- 關閉自動提交;
- 把事務隔離級別設為RR;
- show session variables like "autocommit";
- show session variables like "tx_isolation";
不放心的話,可以用上面兩個語句查詢確認。
第三步:多個終端session模擬并發事務
如上圖,用SecureCRT開啟兩個窗口:
- 窗口A,先啟動事務,并插入記錄;
- 窗口B,再啟動事務,也插入記錄;
- 窗口A,修改插入的記錄;
- 窗口B,也修改插入的記錄;
奇怪的現象發生了,如果并發事務的update語句:
- 更新條件是cell,就會發生異常;
- 更新條件是id,就一切正常;
按道理,插入不沖突的記錄,然后修改這條記錄,行鎖不應該沖突呀?唯一索引,主鍵索引怎么會有差異呢?是否有關?是死鎖,還是其他原因?
大家幫忙分析分析,到底問題在哪里呢?
【本文為51CTO專欄作者“58沈劍”原創稿件,轉載請聯系原作者】