項目經常會出現mysql的死鎖問題,當年年少總是想通過SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS; 查看被鎖的事務,然后kill掉他,或者重啟mysql,唉,治標不治本啊,下次還會出現這些問題,其實造成死鎖大多數情況就是我們的sql寫的不大好。
我們先來模擬一組死鎖,然后通過查看死鎖日志再反向推出死鎖的原因。
首先我們新建一個表:
CREATE TABLE `t1` (
`a` int(11) NOT NULL DEFAULT '0',
`b` varchar(20) DEFAULT NULL,
`c` varchar(100) DEFAULT NULL,
PRIMARY KEY (`a`),
KEY `a_mes` (`b`) USING HASH
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
這里引擎用InnoDB,并且在b字段建了一個索引‘a_mes’,先在表里加幾行數據:
INSERT INTO `test`.`t1` (`a`, `b`, `c`) VALUES ('1', 'aaa', NULL);
INSERT INTO `test`.`t1` (`a`, `b`, `c`) VALUES ('2', 'asd', NULL);
INSERT INTO `test`.`t1` (`a`, `b`, `c`) VALUES ('3', 'axd', NULL);
INSERT INTO `test`.`t1` (`a`, `b`, `c`) VALUES ('4', 'bbb', NULL);
然后我這里的事務級別是RR,可以查看下,select @@tx_isolation;
這里有個姿勢點,事務級別是RR的時候,當進行IUD操作的時候,如果指定的是非唯一索引的那個字段,RR級別下,會加GAP鎖,比如我這里b字段上有非唯一索引,當我執行語句DELETE FROM t1 WHERE b='aaa';的時候就會加GAP鎖,也就是相當于在0-aaa,aaa-asd的范圍加上了GAP鎖,同時aaa也會加X鎖。所以接下來模擬兩個事務:
事務一:set autocommit=0;
1.DELETE FROM t1 WHERE b='aaa';
2.INSERT INTO t1 VALUES(19,'baa','1');
事務二:set autocommit=0;
1.DELETE FROM t1 WHERE b='bbb';
2.INSERT INTO t1 VALUES(16,'aab','1');
當事務一執行第1條語句時,正如前面所說aaa索引加了X鎖,并且在aaa范圍內加了GAP鎖,這時事務二執行弟1條語句,同理bbb索引也會加了X鎖,并且在axd-bbb,bbb-無窮大范圍加了GAP鎖,然后事務一繼續執行第二條語句,這個時候由于事務二加了鎖,事務一就等待鎖,事務二執行弟2條語句后,就報錯了,這個就模擬了一組死鎖。
然后我們查看死鎖日志:show engine innodb status \G
通過日志我們看到事務一在執行INSERT INTO t1 VALUES(19,'baa','1');這句的時候,他要去獲得索引‘a_mes’上的X鎖,但是獲得不到,所以在waiting。。。而事務二,在索引a_mes上持有X鎖,并且事務二在執行INSERT INTO t1 VALUES(16,'aab','1');這句的時候也是獲得不到鎖,然后處于waiting狀態。。。
會查看死鎖日志,懂得mysql的加鎖機制,去反推業務的邏輯,去查找真正造成死鎖的原因,然后解決他,如果不了解GAP鎖推薦這篇文章,寫的很好http://hedengcheng.com/?p=771#_Toc374698322