本文主要內(nèi)容:
介紹InnoDB中的鎖的類型(X莽鸭、S、IX吃靠、IS)硫眨。
解釋為什么引入意向鎖
行鎖的三種算法:Record Lock,Gap Lock巢块,Next-key Lock
一礁阁、InnoDB存儲(chǔ)引擎中的鎖
鎖,在現(xiàn)實(shí)生活中是為我們想要隱藏于外界所使用的一種工具族奢。在計(jì)算機(jī)中姥闭,是協(xié)調(diào)多個(gè)進(jìn)程或縣城并發(fā)訪問某一資源的一種機(jī)制。在數(shù)據(jù)庫(kù)當(dāng)中越走,除了傳統(tǒng)的計(jì)算資源(CPU棚品、RAM、I/O等等)的爭(zhēng)用之外廊敌,數(shù)據(jù)也是一種供許多用戶共享訪問的資源铜跑。如何保證數(shù)據(jù)并發(fā)訪問的一致性、有效性骡澈,是所有數(shù)據(jù)庫(kù)必須解決的一個(gè)問題锅纺,鎖的沖突也是影響數(shù)據(jù)庫(kù)并發(fā)訪問性能的一個(gè)重要因素。從這一角度來說肋殴,鎖對(duì)于數(shù)據(jù)庫(kù)而言就顯得尤為重要伞广。
相對(duì)于其他的數(shù)據(jù)庫(kù)而言拣帽,MySQL的鎖機(jī)制比較簡(jiǎn)單,最顯著的特點(diǎn)就是不同的存儲(chǔ)引擎支持不同的鎖機(jī)制嚼锄。根據(jù)不同的存儲(chǔ)引擎减拭,MySQL中鎖的特性可以大致歸納如下:
這里鎖的對(duì)象是事務(wù),用來鎖定數(shù)據(jù)庫(kù)中的對(duì)象区丑,如:表拧粪、頁(yè)、行沧侥。并且一般鎖的對(duì)象僅在事務(wù)commit或rollback后進(jìn)行釋放可霎。并且有死鎖機(jī)制。
下面我們看InnoDB存儲(chǔ)引擎中兩種標(biāo)準(zhǔn)的行級(jí)鎖:
- 共享鎖(S Lock)宴杀,允許事務(wù)讀一行數(shù)據(jù)
- 排它鎖(X Lock)癣朗,允許事務(wù)刪除或更新一行數(shù)據(jù)
若事務(wù)T對(duì)數(shù)據(jù)對(duì)象A加上S鎖,則事務(wù)T可以讀A但不能修改A旺罢,其他事務(wù)只能再對(duì)A加S鎖旷余,而不能加X鎖,直到T釋放A上的S鎖扁达。這保證了其他事務(wù)可以讀A正卧,但在T釋放A上的S鎖之前不能對(duì)A做任何修改。
若事務(wù)T對(duì)數(shù)據(jù)對(duì)象A加上X鎖跪解,事務(wù)T可以讀A也可以修改A炉旷,其他事務(wù)不能再對(duì)A加任何鎖,直到T釋放A上的鎖叉讥。
上述情況稱為鎖不兼容窘行。
此外,InnoDB存儲(chǔ)引擎還支持多粒度鎖定图仓,這種鎖定允許事務(wù)在行級(jí)上的鎖和表級(jí)上的鎖同時(shí)存在抽高。為了支持在不同粒度上進(jìn)行加鎖操作,InnoDB存儲(chǔ)引擎支持一種額外的鎖方式透绩,稱之為意向鎖。
所謂意向鎖壁熄,就是將要鎖定的對(duì)象分成多個(gè)層次帚豪,意向鎖意味著事務(wù)希望在更細(xì)粒度上進(jìn)行加鎖。如果把上鎖的對(duì)象看成樹形結(jié)構(gòu)(從根到葉為從粗粒度到細(xì)粒度的順序)草丧,那么對(duì)最下層的對(duì)象上鎖狸臣,必須先對(duì)他的上層節(jié)點(diǎn)上鎖。
舉個(gè)例子昌执,比如事務(wù)T要對(duì)某一行R1加X鎖烛亦,必須先對(duì)R1所在的表T1加意向鎖IX(Intention X Lock)诈泼。相應(yīng)的也有IS(Intetion S Lock)鎖。
剛開始我也是很懵的煤禽,不知道引入意向鎖到底是干嘛的铐达。后來再刷書的時(shí)候,才豁然開朗檬果。下面我談下我的理解瓮孙。
因?yàn)橐胍庀蜴i是用來實(shí)現(xiàn)多粒度鎖定的,即行鎖和表鎖同時(shí)存在选脊。我們看看如果不引入意向鎖杭抠,怎么判斷。
如果事務(wù)T要對(duì)表T1加X鎖恳啥,那么這是就必須要判斷T1表下的每一行記錄是否加了S鎖或X鎖(因?yàn)樯厦嫣岬搅随i有不兼容性)偏灿。這樣做效率無疑很低。那么引入意向鎖之后呢钝的?
如果事務(wù)T要對(duì)表T1加X鎖翁垂,在這之前,已經(jīng)有事務(wù)對(duì)表T1中的行記錄R加了S鎖扁藕,那么此時(shí)在表T1上有IS鎖沮峡,當(dāng)事務(wù)T對(duì)表T1準(zhǔn)備加X鎖時(shí),由于X鎖與IS鎖不兼容(關(guān)于兼容性后面會(huì)給出表格)亿柑,所以事務(wù)T要等待行鎖操作完成邢疙。你看,這樣就省去了遍歷的操作望薄,提升了鎖定父節(jié)點(diǎn)(本例為表T1)的效率疟游。
下圖就是X、S痕支、IX颁虐、IS鎖的兼容性了:
二、鎖的算法
InnoDB存儲(chǔ)引擎有3種行鎖的算法卧须,分別是:
- Record Lock:?jiǎn)蝹€(gè)行記錄上的鎖
- Gap Lock:間隙鎖另绩,鎖定一個(gè)范圍,但不包含記錄本身
- Next-Key Lock:Gap Lock+Record Lock花嘶,鎖定一個(gè)范圍笋籽,并且鎖定記錄本身。
舉例說明:
假如一個(gè)索引有10,11,13,20這四個(gè)值椭员。那么該索引可能被Next-Key Locking的區(qū)間為:
(-∞车海,10]
(10,11]
(11,13]
(13,20]
(20,+∞)
注
對(duì)于Next_Key Lock,如果我們鎖定了一個(gè)行隘击,且查詢的索引含有唯一屬性時(shí)(即有唯一索引)侍芝,那么這個(gè)時(shí)候InnoDB會(huì)將Next_Key Lock優(yōu)化成Record Lock研铆,也就是鎖定當(dāng)前行,而不是鎖定當(dāng)前行加一個(gè)范圍州叠;如果我們使用的不是唯一索引鎖定一行數(shù)據(jù)棵红,那么此時(shí)InnoDB就會(huì)按照本來的規(guī)則鎖定一個(gè)范圍和記錄。還有需要注意的點(diǎn)是留量,當(dāng)唯一索引由多個(gè)列組成時(shí)窄赋,如果查詢僅是查找其中的一個(gè)列,這時(shí)候是不會(huì)降級(jí)的楼熄。還有注意的點(diǎn)是忆绰,InnoDB存儲(chǔ)引擎還會(huì)對(duì)輔助索引的下一個(gè)鍵值區(qū)間加上gap lock(這么做也是為了防止幻讀)。Next_Key Lock是為了解決數(shù)據(jù)庫(kù)出現(xiàn)幻讀的問題可岂。
關(guān)于如何加鎖詳見我的這篇文章:Mysql的一致性非鎖定讀和一致性鎖定讀
有關(guān)臟讀错敢、不可重復(fù)讀、幻讀詳見我的這篇文章淺析Mysql的隔離級(jí)別及MVCC
InnoDB存儲(chǔ)引擎默認(rèn)的事務(wù)隔離級(jí)別是RR級(jí)別缕粹,即可重復(fù)讀稚茅。在該級(jí)別下,采用next-key locking的方式加鎖平斩。故而可以防止幻讀現(xiàn)象亚享。
舉例一下,為什么next-key locking 可以解決幻讀問題吧:
所謂幻讀绘面,就是在同一事務(wù)下欺税,連續(xù)執(zhí)行兩次同樣的SQL語(yǔ)句可能導(dǎo)致不同的結(jié)果,第二次的SQL語(yǔ)句可能會(huì)返回之前不存在的行揭璃。
創(chuàng)建表t:
create table t (a int primary key);
insert into t select 1;
insert into t select 2;
insert into t select 5;
這時(shí)有三行記錄晚凿,分別是1,2,5。
假設(shè)有如下執(zhí)行序列:
我們分析一下瘦馍,會(huì)話A在時(shí)間2查詢的結(jié)果為5歼秽,由于使用了select...for update語(yǔ)句,為(2情组,+∞)這個(gè)范圍加了X鎖燥筷。因此任何對(duì)于這個(gè)范圍的插入都是不被允許的,由于4在這個(gè)范圍院崇,所以不允許插入肆氓,也就避免了幻讀。
參考資料
《mysql技術(shù)內(nèi)幕--InnoDB存儲(chǔ)引擎》