日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

一次DeleteInsert引发的Mysql死锁

發(fā)布時(shí)間:2024/1/17 数据库 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 一次DeleteInsert引发的Mysql死锁 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

近日遇到一個(gè)比較奇怪的deadlock錯(cuò)誤, 錯(cuò)誤詳情:

Deadlock found when trying to get lock; try restarting transaction; nested exception is com.ibatis.common.jdbc.exception.NestedSQLException...

跟蹤代碼后最終定位到一段業(yè)務(wù)邏輯:

delete from A where no = $no; insert into A(no, value) values($no, "value");

印象中mysql一直是使用行級(jí)鎖, 為什么此處在并發(fā)時(shí)會(huì)發(fā)生死鎖呢? 唯一的解釋是mysql在這邊鎖住的不只一行數(shù)據(jù).

簡單搜索之后, 發(fā)現(xiàn)mysql的鎖分為三種(按照鎖定的行數(shù)劃分):
1.record lock:記錄鎖,也就是僅僅鎖著單獨(dú)的一行
2.gap lock:區(qū)間鎖,僅僅鎖住一個(gè)區(qū)間(注意這里的區(qū)間都是開區(qū)間,也就 是不包括邊界值,至于為什么這么定義?innodb官方定義的)
3.next-key lock:record lock+gap lock,所以next-key lock也就半開半閉區(qū)間,且是下界開,上界閉。(為什么這么定義?innodb官方定義的)

由于此處是在明確指定了no=XX的情況下拋出了死鎖異常, 并且no建立的是普通索引, 所以此處mysql使用的應(yīng)該是next-key lock(查看何種情況下使用何種鎖?https://dev.mysql.com/doc/refman/5.7/en/innodb-locks-set.html).

下面來舉個(gè)手冊(cè)上的例子看看next-key lock是如何上鎖的。假如一個(gè)索引的行有10,11,13,20
那么可能的next-key lock的包括:
(無窮小, 10]
(10,11]
(11,13]
(13,20]
(20, 無窮大)

下面分析何種情況下會(huì)發(fā)生死鎖.
結(jié)合業(yè)務(wù)邏輯, 執(zhí)行新增操作時(shí)也會(huì)執(zhí)行一樣的邏輯, 先進(jìn)行delete.
例如,現(xiàn)在表student中有四條數(shù)據(jù):

Image 1

現(xiàn)在要新增一條數(shù)據(jù), no = 21, 這時(shí)候會(huì)先進(jìn)行delete, 線程會(huì)鎖住(20, 無窮大)這塊區(qū)間, 加入這個(gè)時(shí)候另一個(gè)線程正在新增另一條數(shù)據(jù) no = 22, 線程也會(huì)鎖住(20, 無窮大)這塊區(qū)間就會(huì)發(fā)生死鎖.

下面看具體實(shí)驗(yàn):
創(chuàng)建一張表student.

CREATE TABLE `student` (`id` int(11) NOT NULL AUTO_INCREMENT,`no` int(11) DEFAULT NULL,`name` varchar(255) DEFAULT NULL,PRIMARY KEY (`id`),KEY `idx_no` (`no`) ) ENGINE=InnoDB AUTO_INCREMENT=22 DEFAULT CHARSET=latin1

插入數(shù)據(jù):

INSERT INTO student (no,name) VALUES(10, "Jim"); INSERT INTO student (no,name) VALUES(11, "Kimi"); INSERT INTO student (no,name) VALUES(13, "Tom"); INSERT INTO student (no,name) VALUES(20, "Mike");

Image 2

執(zhí)行兩個(gè)事務(wù):
session 1:

begin; delete from student where no = 21;

session 2:

begin; delete from student where no = 22;

此處解釋一下, 此時(shí),session 1和session 2都會(huì)對(duì)區(qū)間(20, 無窮大)加鎖, 而區(qū)間鎖只是用來防止其他事務(wù)在區(qū)間中插入數(shù)據(jù),區(qū)間x鎖 與區(qū)間S鎖效果是一樣的(只要不是插入操作), 因此兩個(gè)session都會(huì)持有鎖.

參考:https://dev.mysql.com/doc/refman/5.1/en/innodb-record-level-locks.html
Gap locks in InnoDB are “purely inhibitive”, which means they only stop other transactions from inserting to the gap. Thus, a gap X-lock has the same effect as a gap S-lock.

繼續(xù)執(zhí)行:
session 1:

INSERT INTO student (no,name) VALUES(21, "Zhoubing");

此時(shí)session 1阻塞(因?yàn)閟ession 2持有區(qū)間鎖), 如圖:

Image 3

session 2:

INSERT INTO student (no,name) VALUES(22, "Zhoubing");

此時(shí)session 2死鎖(因?yàn)閟ession 1持有區(qū)間鎖), 如圖:

Image 4

.

總結(jié), delete之后進(jìn)行insert有可能發(fā)生死鎖, 因?yàn)閐elete可能會(huì)持有區(qū)間鎖, 而區(qū)間鎖是可重入的(只要不是插入數(shù)據(jù)).

解決方案:
將事務(wù)隔離級(jí)別將為read commit.

?

參考資料:
1.Next-Key Locks
2.Locks Set by Different SQL Statements in InnoDB

總結(jié)

以上是生活随笔為你收集整理的一次DeleteInsert引发的Mysql死锁的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。