死鎖報(bào)錯日志:
Deadlock found when trying to get lock; try restarting transaction
死鎖報(bào)錯示例:
1、各自鎖住對方進(jìn)程正在使用的行數(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;
因?yàn)閟ession2中的 dbid<20 會先對小于9的數(shù)據(jù)先加鎖(id-9已經(jīng)被session1提前加鎖)巩掺,此時(shí)session1再對id-1的數(shù)據(jù)執(zhí)行加鎖,就會產(chǎn)生爭用睬愤,從而產(chǎn)生死鎖待榔。
2屁使、批量入庫减细,不存在則新增匆瓜,存在則更新的情況:
假設(shè)目前 students 表中不存在 dbid 為 19 和 20 的數(shù)據(jù),此時(shí)需要新增
-- 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)生死鎖驮吱,是因?yàn)椋?/p>
- 當(dāng)對已存在的行進(jìn)行鎖定時(shí)(主鍵),mysql就只有行鎖萧吠。
- 當(dāng)對未存在的行進(jìn)行鎖的時(shí)候(即使條件為主鍵)左冬,mysql會鎖住一段范圍(即gap鎖),其鎖范圍為:
(無窮小或小于表中鎖住id的最大值纸型,無窮大或大于表中鎖住id的最小值)
如果表中目前有已有的id為18拇砰,那么就鎖住 [19,無窮大)
;
如果表中目前已有的 id 區(qū)間為(11 狰腌, 30)毕匀,那么就鎖住[12,29]
癌别;
所以示例2中的兩個session其實(shí)鎖住的是相同 gap 中的數(shù)據(jù),因此執(zhí)行插入時(shí)才會產(chǎn)生 dead-lock蹋笼;對于這種此類示例2的死鎖解決辦法是用mysql特有的語法 ON DUPLICATE KEY UPDATE
來解決此問題展姐;
該語法的意思為:
- 在insert時(shí)候,如果insert的數(shù)據(jù)會引起唯一索引(包括主鍵索引)的沖突剖毯,即唯一值重復(fù)了圾笨,則不會執(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;
因?yàn)橄鄬τ谥麈I來說擂达,insert語句,插入的行不管是否存在胶滋,都只有行鎖板鬓。
就到這里吧悲敷!