死鎖報錯日志:
Deadlock found when trying to get lock; try restarting transaction
死鎖報錯示例:
1、各自鎖住對方進程正在使用的行數(shù)據(jù)
譬如先執(zhí)行:
-- session1
BEGIN;
UPDATE students SET memo='DLLock' WHERE dbid =9;
-- session2
BEGIN;
UPDATE students SET memo='DLLock' WHERE dbid =7;
再執(zhí)行:
-- session1
UPDATE students SET memo='DLLock' WHERE dbid =7;
-- session2
UPDATE students SET memo='DLLock' WHERE dbid =9;
再比如下面的情況:
-- session1
BEGIN;
UPDATE students SET memo='DLLock' WHERE dbid =9;
-- session2
BEGIN;
SELECT dbid FROM students WHERE dbid<20 for UPDATE;
執(zhí)行完以上后再在session1中執(zhí)行如下,即可產(chǎn)生死鎖:
UPDATE students SET memo='DdLock' WHERE dbid =1;
因為session2中的 dbid<20 會先對小于9的數(shù)據(jù)先加鎖(id-9已經(jīng)被session1提前加鎖),此時session1再對id-1的數(shù)據(jù)執(zhí)行加鎖,就會產(chǎn)生爭用,從而產(chǎn)生死鎖。
2、批量入庫,不存在則新增,存在則更新的情況:
假設(shè)目前 students 表中不存在 dbid 為 19 和 20 的數(shù)據(jù),此時需要新增
-- session1
BEGIN;
SELECT * FROM students WHERE dbid =19 for UPDATE;
-- session2
BEGIN;
SELECT * FROM students WHERE dbid =20 for UPDATE;
執(zhí)行完以上后再在各自的session中中執(zhí)行插入操作,即可產(chǎn)生死鎖:
-- session1
INSERT INTO `students` (`dbid`, `uid`, `uname`) VALUES (19, 39, '小明明');
-- session2
INSERT INTO `students` (`dbid`, `uid`, `uname`) VALUES (20, 35, '小紅紅');
為何兩條不存在的數(shù)據(jù)也會產(chǎn)生死鎖,是因為:
- 當對已存在的行進行鎖定時(主鍵),mysql就只有行鎖。
- 當對未存在的行進行鎖的時候(即使條件為主鍵),mysql會鎖住一段范圍(即gap鎖),其鎖范圍為:
(無窮小或小于表中鎖住id的最大值,無窮大或大于表中鎖住id的最小值)
如果表中目前有已有的id為18,那么就鎖住 [19,無窮大);
如果表中目前已有的 id 區(qū)間為(11 , 30),那么就鎖住[12,29];
所以示例2中的兩個session其實鎖住的是相同 gap 中的數(shù)據(jù),因此執(zhí)行插入時才會產(chǎn)生 dead-lock;對于這種此類示例2的死鎖解決辦法是用mysql特有的語法 ON DUPLICATE KEY UPDATE來解決此問題;
該語法的意思為:
- 在insert時候,如果insert的數(shù)據(jù)會引起唯一索引(包括主鍵索引)的沖突,即唯一值重復了,則不會執(zhí)行insert操作,而執(zhí)行后面的update操作。
如下兩條語句最終的執(zhí)行效果相同:
-- 1
INSERT INTO table (a,b,c) VALUES (1,2,3) ON DUPLICATE KEY UPDATE c=c+1;
-- 2
UPDATE table SET c=c+1 WHERE a=1;
因為相對于主鍵來說,insert語句,插入的行不管是否存在,都只有行鎖。
就到這里吧!