mysql 插入加锁_Mysql加锁过程详解(9)-innodb下的记录锁,间隙锁,next-key锁
你需要知道的
之前我們介紹了排他鎖,其實(shí)innodb下的記錄鎖(也叫行鎖),間隙鎖,next-key鎖統(tǒng)統(tǒng)屬于排他鎖。
行鎖
記錄鎖其實(shí)很好理解,對(duì)表中的記錄加鎖,叫做記錄鎖,簡(jiǎn)稱行鎖。
生活中的間隙鎖
編程的思想源于生活,生活中的例子能幫助我們更好的理解一些編程中的思想。
生活中排隊(duì)的場(chǎng)景,小明,小紅,小花三個(gè)人依次站成一排,此時(shí),如何讓新來(lái)的小剛不能站在小紅旁邊,這時(shí)候只要將小紅和她前面的小明之間的空隙封鎖,將小紅和她后面的小花之間的空隙封鎖,那么小剛就不能站到小紅的旁邊。
這里的小紅,小明,小花,小剛就是數(shù)據(jù)庫(kù)的一條條記錄。
他們之間的空隙也就是間隙,而封鎖他們之間距離的鎖,叫做間隙鎖。
Mysql中的間隙鎖
下表中(見(jiàn)圖一),id為主鍵,number字段上有非唯一索引的二級(jí)索引,有什么方式可以讓該表不能再插入number=5的記錄?
圖一
根據(jù)上面生活中的例子,我們自然而然可以想到,只要控制幾個(gè)點(diǎn),number=5之前不能插入記錄,number=5現(xiàn)有的記錄之間不能再插入新的記錄,number=5之后不能插入新的記錄,那么新的number=5的記錄將不能被插入進(jìn)來(lái)。
那么,mysql是如何控制number=5之前,之中,之后不能有新的記錄插入呢(防止幻讀)?
答案是用間隙鎖,在RR級(jí)別下,mysql通過(guò)間隙鎖可以實(shí)現(xiàn)鎖定number=5之前的間隙,number=5記錄之間的間隙,number=5之后的間隙,從而使的新的記錄無(wú)法被插入進(jìn)來(lái)。
間隙是怎么劃分的?
注:為了方面理解,我們規(guī)定(id=A,number=B)代表一條字段id=A,字段number=B的記錄,(C,D)代表一個(gè)區(qū)間,代表C-D這個(gè)區(qū)間范圍。
圖一中,根據(jù)number列,我們可以分為幾個(gè)區(qū)間:(無(wú)窮小,2),(2,4),(4,5),(5,5),(5,11),(11,無(wú)窮大)。
只要這些區(qū)間對(duì)應(yīng)的兩個(gè)臨界記錄中間可以插入記錄,就認(rèn)為區(qū)間對(duì)應(yīng)的記錄之間有間隙。
例如:區(qū)間(2,4)分別對(duì)應(yīng)的臨界記錄是(id=1,number=2),(id=3,number=4),這兩條記錄中間可以插入(id=2,number=3)等記錄,那么就認(rèn)為(id=1,number=2)與(id=3,number=4)之間存在間隙。
很多人會(huì)問(wèn),那記錄(id=6,number=5)與(id=8,number=5)之間有間隙嗎?
答案是有的,(id=6,number=5)與(id=8,number=5)之間可以插入記錄(id=7,number=5),因此(id=6,number=5)與(id=8,number=5)之間有間隙的,
間隙鎖鎖定的區(qū)域
根據(jù)檢索條件向左尋找最靠近檢索條件的記錄值A(chǔ),作為左區(qū)間,向右尋找最靠近檢索條件的記錄值B作為右區(qū)間,即鎖定的間隙為(A,B)。
圖一中,where number=5的話,那么間隙鎖的區(qū)間范圍為(4,11);
間隙鎖的目的是為了防止幻讀,其主要通過(guò)兩個(gè)方面實(shí)現(xiàn)這個(gè)目的:
(1)防止間隙內(nèi)有新數(shù)據(jù)被插入
(2)防止已存在的數(shù)據(jù),更新成間隙內(nèi)的數(shù)據(jù)(例如防止numer=3的記錄通過(guò)update變成number=5)
innodb自動(dòng)使用間隙鎖的條件:
(1)必須在RR級(jí)別下
(2)檢索條件必須有索引(沒(méi)有索引的話,mysql會(huì)全表掃描,那樣會(huì)鎖定整張表所有的記錄,包括不存在的記錄,此時(shí)其他事務(wù)不能修改不能刪除不能添加)
接下來(lái),通過(guò)實(shí)際操作觀察下間隙鎖的作用范圍
圖三 表結(jié)構(gòu)
案例一:
````
session1:
starttransaction;select * from news where number=4 for update;
session2:
starttransaction;insert into news value(2,4);#(阻塞)insert into news value(2,2);#(阻塞)insert into news value(4,4);#(阻塞)insert into news value(4,5);#(阻塞)insert into news value(7,5);#(執(zhí)行成功)insert into news value(9,5);#(執(zhí)行成功)insert into news value(11,5);#(執(zhí)行成功)
````
檢索條件number=4,向左取得最靠近的值2作為左區(qū)間,向右取得最靠近的5作為右區(qū)間,因此,session 1的間隙鎖的范圍(2,4),(4,5),如下圖所示:
間隙鎖鎖定的區(qū)間為(2,4),(4,5),即記錄(id=1,number=2)和記錄(id=3,number=4)之間間隙會(huì)被鎖定,記錄(id=3,number=4)和記錄(id=6,number=5)之間間隙被鎖定。
因此記錄(id=2,number=4),(id=2,number=2),(id=4,number=4),(id=4,number=5)正好處在(id=3,number=4)和(id=6,number=5)之間,所以插入不了,需要等待鎖的釋放,而記錄(id=7,number=5),(id=9,number=5),(id=11,number=5)不在上述鎖定的范圍內(nèi),因此都會(huì)插入成功。
案例二:
````
session1:
starttransaction;select * from news where number=13 for update;
session2:
starttransaction;insert into news value(11,5);#(執(zhí)行成功)insert into news value(12,11);#(執(zhí)行成功)insert into news value(14,11);#(阻塞)insert into news value(15,12);#(阻塞)update news set id=14 where number=11;#(阻塞)update news set id=11 where number=11;#(執(zhí)行成功)
````
檢索條件number=13,向左取得最靠近的值11作為左區(qū)間,向右由于沒(méi)有記錄因此取得無(wú)窮大作為右區(qū)間,因此,session 1的間隙鎖的范圍(11,無(wú)窮大),如下圖所示:
此表中沒(méi)有number=13的記錄的,innodb依然會(huì)為該記錄左右兩側(cè)加間隙鎖,間隙鎖的范圍(11,無(wú)窮大)。
有人會(huì)問(wèn),為啥update news set id=14 where number=11會(huì)阻塞,但是update news set id=11 where number=11卻執(zhí)行成功呢?
間隙鎖采用在指定記錄的前面和后面以及中間的間隙上加間隙鎖的方式避免數(shù)據(jù)被插入,此圖間隙鎖鎖定的區(qū)域是(11,無(wú)窮大),也就是記錄(id=13,number=11)之后不能再插入記錄,update news set id=14 where number=11這條語(yǔ)句如果執(zhí)行的話,將會(huì)被插入到(id=13,number=11)的后面,也就是在區(qū)間(11,無(wú)窮大)之間,由于該區(qū)間被間隙鎖鎖定,所以只能阻塞等待,而update news set id=11 where number=11執(zhí)行后是會(huì)被插入到(id=13,number=11)的記錄前面,也就不在(11,無(wú)窮大)的范圍內(nèi),所以無(wú)需等待,執(zhí)行成功。
案例三:
````
session1:
starttransaction;select * from news where number=5 for update;
session2:
starttransaction;insert into news value(4,4);#(阻塞)insert into news value(4,5);#(阻塞)insert into news value(5,5);#(阻塞)insert into news value(7,11);#(阻塞)insert into news value(9,12);#(執(zhí)行成功)insert into news value(12,11);#(阻塞)update news set number=5 where id=1;#(阻塞)update news set id=11 where number=11;#(阻塞)update news set id=2 where number=4;#(執(zhí)行成功)update news set id=4 where number=4;#(阻塞)
````
檢索條件number=5,向左取得最靠近的值4作為左區(qū)間,向右取得11為右區(qū)間,因此,session 1的間隙鎖的范圍(4,5),(5,11),如下圖所示:
有人會(huì)問(wèn),為啥insert into news value(9,12)會(huì)執(zhí)行成功?間隙鎖采用在指定記錄的前面和后面以及中間的間隙上加間隙鎖的方式避免數(shù)據(jù)被插入,(id=9,number=12)很明顯在記錄(13,11)的后面,因此不再鎖定的間隙范圍內(nèi)。
為啥update news set number=5 where id=1會(huì)阻塞?
number=5的記錄的前面,后面包括中間都被封鎖了,你這個(gè)update news set number=5 where id=1根本沒(méi)法執(zhí)行,因?yàn)閕nnodb已經(jīng)把你可以存放的位置都鎖定了,因?yàn)橹荒艿却?/p>
同理,update news set id=11 where number=11由于記錄(id=10,number=5)與記錄(id=13,number=11)中間的間隙被封鎖了,你這句sql也沒(méi)法執(zhí)行,必須等待,因?yàn)榇娣诺奈恢帽环怄i了。
案例四:
session 1:
starttransaction;select * from news where number>4 for update;
session2:
starttransaction;update news set id=2 where number=4;#(執(zhí)行成功)update news set id=4 where number=4;#(阻塞)update news set id=5 where number=5;#(阻塞)insert into news value(2,3);#(執(zhí)行成功)insert into news value(null,13);#(阻塞)
檢索條件number>4,向左取得最靠近的值4作為左區(qū)間,向右取無(wú)窮大,因此,session 1的間隙鎖的范圍(4,無(wú)窮大),如下圖所示:
session2中之所以有些阻塞,有些執(zhí)行成功,其實(shí)就是因?yàn)椴迦氲膮^(qū)域被鎖定,從而阻塞。
next-key鎖
next-key鎖其實(shí)包含了記錄鎖和間隙鎖,即鎖定一個(gè)范圍,并且鎖定記錄本身,InnoDB默認(rèn)加鎖方式是next-key 鎖。
上面的案例一session 1中的sql是:select * from news where number=4 for update ;
next-key鎖鎖定的范圍為間隙鎖+記錄鎖,即區(qū)間(2,4),(4,5)加間隙鎖,同時(shí)number=4的記錄加記錄鎖。
總結(jié)
以上是生活随笔為你收集整理的mysql 插入加锁_Mysql加锁过程详解(9)-innodb下的记录锁,间隙锁,next-key锁的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 如何使用DPA华为备份一体机备份达梦数据
- 下一篇: 对mysql的总结与反思_一次DB故障引