拿捏削罩!隔離級別盗蟆、幻讀节值、Gap Lock、Next-Key Lock

前面我寫了很多Mysql相關的知識點榜聂,到這一篇稍微可以串一下了搞疗,從SQL執(zhí)行流程、MVCC到鎖须肆,很多時候可能覺得對于間隙鎖和Next-Key Lock好像已經理解了匿乃,但是好像又覺得理解差那么一點意思,這篇文章從頭來梳理一下概念豌汇,明確一下這些知識幢炸。

首先,對于Mysql來說實現了兩種行級鎖:

共享鎖:允許事務讀一行數據拒贱,一般記為S宛徊,也稱為讀鎖

排他鎖:允許事務刪除或者更新一行數據,一般記為X柜思,也稱為寫鎖

關于讀寫鎖的互斥性岩调,應該都很清楚,讀鎖只能和讀鎖兼容赡盘,其他場景都無法兼容号枕,這里不再贅述吧。

隔離級別

繼續(xù)回顧下關于Mysql的4個隔離級別:

讀未提交Read Uncommitted:能讀到其他事務還沒有提交的數據陨享,這種現象叫做臟讀葱淳。

讀已提交Read Committed:只會讀取其他事務已經提交的數據,所以不會產生RC的臟讀問題抛姑。所以又帶來一個問題叫做不可重復讀赞厕,一個事務中兩次一樣的SQL查詢可能查到的結果不一樣。

可重復讀Repeatable Read:RR是Mysql的默認隔離級別定硝,一個事務中兩次SQL查詢總是會查到一樣的結果皿桑,不存在不可重復讀的問題,但是還是會有幻讀的問題。

串行Serializable:串行場景沒有任何問題诲侮,完全串行化的操作镀虐,讀加讀鎖,寫加寫鎖沟绪。

image

幻讀刮便、Next-Key Lock、MVCC

簡單的回顧完了基礎绽慈,那么我們看看RR級別下還會存在的幻讀到底是什么問題恨旱,Mysql官方文檔這樣描述的:

The so-called phantom problem occurs within a transaction when the same query produces different sets of rows at different times. For example, if a SELECT is executed twice, but returns a row the second time that was not returned the first time, the row is a “phantom” row.

翻譯過來就是,幻讀指的是同一事務下坝疼,不同的時間點搜贤,同樣的查詢,得到不同的行記錄的集合裙士。

如果說一個select執(zhí)行了兩次入客,但是第二次比第一次多出來行記錄,這就是幻讀腿椎。

所以,對于幻讀來說那一定是新增插入的數據夭咬!

比如說在一個事務內啃炸,先查詢select * from user where age=10 for update,得到的結果是id為[1,2,3]的記錄卓舵,再次執(zhí)行查詢南用,得到了結果為[1,2,3,4]的記錄,這是幻讀掏湾。

那怎么解決幻讀的問題裹虫?以前我在文章里說解決幻讀的原理是MVCC(MVCC原理看這里)很多網上的文章也有這么寫的,其實不能說錯融击,但是肯定也是不太對的筑公,準確地來說應該是通過MVCC+Next-Key Lock的方式才解決了幻讀的問題。

對于MVCC中的讀可以分為兩種尊浪,分別叫做快照讀當前讀(這個當前讀的說法我在書里翻了半天也沒有找到匣屡,但是看網上一堆資料和大佬都叫當前讀,那么我們就叫當前讀吧拇涤,你知道的話可以告訴我哪本書有這個稱呼捣作,Mysql我只看見Lock reading或者鎖定讀的叫法,有的也說鎖定讀就是當前讀鹅士,但是并沒有找到當前讀這種稱呼的出處在哪兒)券躁。

快照讀就是簡單的select查詢,查詢的都是快照版本,這個場景下因為都是基于MVCC來查詢快照的某個版本也拜,所以不會存在幻讀的問題旭贬,也可以認為是解決了幻讀的方案之一,對于RC級別來說搪泳,因為每次查詢都重新生成一個read view稀轨,也就是查詢的都是最新的快照數據,所以會可能每次查詢到不一樣的數據岸军,造成不可重復讀奋刽,而對于RR級別來說只有第一次的時候生成read view,查詢的是事務開始的時候的快照數據艰赞,所以就不存在不可重復讀的問題佣谐,當然就更不可能有幻讀的問題了。

所以方妖,現在我們說幻讀狭魂,其實不是指快照讀的場景,而是指的是當前讀的場景党觅。

當前讀指的是lock in share mode雌澄、for updateinsert杯瞻、update镐牺、delete這些需要加鎖的操作。對于MVCC來說就是解決的快照讀的場景魁莉,而對于當前讀那么就是Next-Key Lock要解決的事情睬涧。

那么Next-Key Lock是什么?怎么解決的幻讀旗唁?

行鎖有寫鎖X和讀鎖S兩種畦浓,實際上行鎖有3種實現算法,Next-Key Lock是其中之一检疫。

第一種叫做Record Lock讶请,字面意思,行記錄的鎖电谣,實際上指的是對索引記錄的鎖定秽梅。

比如執(zhí)行語句select * from user where age=10 for update,將會鎖住user表所有age=10的行記錄剿牺,所有對age=10的記錄的操作都會被阻塞企垦。

第二種都比較熟悉,叫做Gap Lock晒来,也就是間隙鎖钞诡,它用于鎖定的索引之間的間隙,但是不會包含記錄本身。

比如語句select * from user where age>1 and age<10 for update荧降,將會鎖住age在(1,10)的范圍區(qū)間接箫,此時其他事務對該區(qū)間的操作都會被阻塞。

間隙鎖是可重復讀RR隔離級別下特有的朵诫,另外還有幾種場景也會不使用間隙鎖辛友。

  1. 事務隔離級別設置為不可重復讀RC ,這樣肯定沒有間隙鎖了剪返。

  2. Innodb_locks_unsafe_for_binlog設置為1

  3. 另外一種情況適用于主鍵索引或者唯一索引的等值查詢條件废累,比如select * from user where id=1id是主鍵索引脱盲,這樣只使用Record Lock就可以了邑滨,因為能唯一鎖定一條記錄,所以沒有必要再加間隙鎖了钱反,這是鎖降級的過程掖看。

而第三種Next-Key Lock實際上就是相當于Record Lock+Gap Lock的組合。比如索引有10面哥,20哎壳,30幾個值,那么被鎖住的區(qū)間可能會是(-∞,10]幢竹,(10,20]耳峦,(20,30],(30,+∞)焕毫。

解決幻讀

上一篇關于更新SQL執(zhí)行過程我們已經對這個基礎有了一定的了解,在這里我們去掉和這里內容無關的一些日志的細節(jié)驶乾,把給數據加鎖的流程加入進去邑飒,這樣通過SQL執(zhí)行可以更好地理解Next-Key Lock到底是如何解決幻讀的,執(zhí)行過程如下:

image
  1. 首先第一步Server層會來查詢數據
  2. 存儲引擎根據查詢條件查到數據之后對數據進行加鎖级乐,Record Lock或者間隙鎖疙咸,然后返回數據
  3. Server層拿到數據之后調用API去存儲引擎更新數據
  4. 最后存儲引擎返回結果,流程結束

搞一張表說明一下风科,user表有4個字段撒轮,id是主鍵索引,name是唯一索引贼穆,age是普通索引题山,city沒有索引,然后插入一些測試數據故痊,下面區(qū)分一下幾種情況來說明是怎么加Next-Key Lock的顶瞳,然后就知道為啥會沒有幻讀的問題了。

image

沒有索引

更新語句update user set city='nanjing' where city='wuhan'會發(fā)生什么?

因為city是沒有索引的慨菱,所以存儲引擎只能給所有的記錄都加上鎖焰络,然后把數據都返回給Server層,然后Server層把city改成nanjing符喝,再更新數據闪彼。

因此,首先Record Lock會鎖住現有的7條記錄协饲,間隙鎖則會對主鍵索引的間隙全部加上間隙鎖畏腕。

所以,更新的時候沒有索引是非炒鸦可怕的一件事情郊尝,相當于把整個表都給鎖了,那表都給鎖了當然不存在幻讀了战惊。

image

普通索引

我們再假設一個語句select * from user where age=20 for update流昏。

因為age是一個普通索引,存儲引擎根據條件過濾查到所有匹配age=20的記錄吞获,給他們加上寫鎖况凉,間隙鎖會加在(10,20),(20,30)的區(qū)間上各拷,因此現在無論怎樣都無法插入age=20的記錄了

為什么要鎖定這兩個區(qū)間刁绒?如果不鎖定這兩個區(qū)間的話,那么還能插入比如id=11,age=20或者id=21,age=20的記錄烤黍,這樣就存在幻讀了知市。

(那實際上寫鎖不光是在會加在age普通索引上,還會加在主鍵索引上速蕊,因為數據都是在主鍵索引下對吧嫂丙,這個肯定也要加鎖的,為了看起來簡單點规哲,就不畫出來了)

image

唯一&主鍵索引

如果查詢的是唯一索引又會發(fā)生什么呢跟啤?比如有查詢語句select * from user where name='b' for update

上面我們提到過唉锌,如果是唯一索引或者主鍵索引的話隅肥,并且是等值查詢,實際上會發(fā)生鎖降級袄简,降級為Record Lock腥放,就不會有間隙鎖了。

因為主鍵或者唯一索引能保證值是唯一的痘番,所以也就不需要再增加間隙鎖了捉片。

很顯然平痰,是無法插入name=b的的記錄的,也不存在幻讀問題伍纫。

如果是范圍查詢比如id>1 and id<11呢宗雇,實際上也是一樣的鎖定方式,不再贅述莹规。

相比稍微有點不同的是上面也說過赔蒲,唯一索引不光鎖定唯一索引,還會鎖定主鍵索引良漱,主鍵索引的話只要索引主鍵索引就行了舞虱。

image

總結

那最后說了這么多,RR級別下不是都已經解決了幻讀的問題嗎母市,怎么還說有幻讀的問題呢矾兜?

關于這個問題,可以看看這個報出的BUGhttps://bugs.mysql.com/bug.php?id=63870患久,回復說了這不是BUG椅寺,這是符合隔離規(guī)范的設計,有興趣的自己看看吧。

image
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市抹镊,隨后出現的幾起案子,更是在濱河造成了極大的恐慌刁标,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,383評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡链韭,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 90,522評論 3 385
  • 文/潘曉璐 我一進店門煮落,熙熙樓的掌柜王于貴愁眉苦臉地迎上來梧油,“玉大人,你說我怎么就攤上這事州邢。” “怎么了褪子?”我有些...
    開封第一講書人閱讀 157,852評論 0 348
  • 文/不壞的土叔 我叫張陵量淌,是天一觀的道長。 經常有香客問我嫌褪,道長呀枢,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,621評論 1 284
  • 正文 為了忘掉前任笼痛,我火速辦了婚禮裙秋,結果婚禮上琅拌,老公的妹妹穿的比我還像新娘。我一直安慰自己摘刑,他們只是感情好进宝,可當我...
    茶點故事閱讀 65,741評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著枷恕,像睡著了一般党晋。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上徐块,一...
    開封第一講書人閱讀 49,929評論 1 290
  • 那天未玻,我揣著相機與錄音,去河邊找鬼胡控。 笑死扳剿,一個胖子當著我的面吹牛,可吹牛的內容都是我干的昼激。 我是一名探鬼主播庇绽,決...
    沈念sama閱讀 39,076評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼癣猾!你這毒婦竟也來了敛劝?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,803評論 0 268
  • 序言:老撾萬榮一對情侶失蹤纷宇,失蹤者是張志新(化名)和其女友劉穎夸盟,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體像捶,經...
    沈念sama閱讀 44,265評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡上陕,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,582評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了拓春。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片释簿。...
    茶點故事閱讀 38,716評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖硼莽,靈堂內的尸體忽然破棺而出庶溶,到底是詐尸還是另有隱情,我是刑警寧澤懂鸵,帶...
    沈念sama閱讀 34,395評論 4 333
  • 正文 年R本政府宣布偏螺,位于F島的核電站,受9級特大地震影響匆光,放射性物質發(fā)生泄漏套像。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,039評論 3 316
  • 文/蒙蒙 一终息、第九天 我趴在偏房一處隱蔽的房頂上張望夺巩。 院中可真熱鬧贞让,春花似錦、人聲如沸柳譬。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽征绎。三九已至蹲姐,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間人柿,已是汗流浹背柴墩。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留凫岖,地道東北人江咳。 一個月前我還...
    沈念sama閱讀 46,488評論 2 361
  • 正文 我出身青樓,卻偏偏與公主長得像哥放,于是被迫代替她去往敵國和親歼指。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,612評論 2 350