<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          拿捏!隔離級別、幻讀、Gap Lock、Next-Key Lock

          共 3863字,需瀏覽 8分鐘

           ·

          2021-08-29 16:38

          首先,對于Mysql來說實現(xiàn)了兩種行級鎖:

          共享鎖:允許事務(wù)讀一行數(shù)據(jù),一般記為S,也稱為讀鎖

          排他鎖:允許事務(wù)刪除或者更新一行數(shù)據(jù),一般記為X,也稱為寫鎖

          關(guān)于讀寫鎖的互斥性,應(yīng)該都很清楚,讀鎖只能和讀鎖兼容,其他場景都無法兼容,這里不再贅述吧。

          隔離級別

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

          讀未提交Read Uncommitted:能讀到其他事務(wù)還沒有提交的數(shù)據(jù),這種現(xiàn)象叫做臟讀。

          讀已提交Read Committed:只會讀取其他事務(wù)已經(jīng)提交的數(shù)據(jù),所以不會產(chǎn)生RC的臟讀問題。所以又帶來一個問題叫做不可重復(fù)讀,一個事務(wù)中兩次一樣的SQL查詢可能查到的結(jié)果不一樣。

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

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

          幻讀、Next-Key Lock、MVCC

          簡單的回顧完了基礎(chǔ),那么我們看看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.

          翻譯過來就是,幻讀指的是同一事務(wù)下,不同的時間點,同樣的查詢,得到不同的行記錄的集合。

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

          所以,對于幻讀來說那一定是新增插入的數(shù)據(jù)!

          比如說在一個事務(wù)內(nèi),先查詢select * from user where age=10 for update,得到的結(jié)果是id為[1,2,3]的記錄,再次執(zhí)行查詢,得到了結(jié)果為[1,2,3,4]的記錄,這是幻讀。

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

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

          快照讀就是簡單的select查詢,查詢的都是快照版本,這個場景下因為都是基于MVCC來查詢快照的某個版本,所以不會存在幻讀的問題,也可以認為是解決了幻讀的方案之一,對于RC級別來說,因為每次查詢都重新生成一個read view,也就是查詢的都是最新的快照數(shù)據(jù),所以會可能每次查詢到不一樣的數(shù)據(jù),造成不可重復(fù)讀,而對于RR級別來說只有第一次的時候生成read view,查詢的是事務(wù)開始的時候的快照數(shù)據(jù),所以就不存在不可重復(fù)讀的問題,當(dāng)然就更不可能有幻讀的問題了。

          所以,現(xiàn)在我們說幻讀,其實不是指快照讀的場景,而是指的是當(dāng)前讀的場景。

          當(dāng)前讀指的是lock in share mode、for updateinsert、updatedelete這些需要加鎖的操作。對于MVCC來說就是解決的快照讀的場景,而對于當(dāng)前讀那么就是Next-Key Lock要解決的事情。

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

          行鎖有寫鎖X和讀鎖S兩種,實際上行鎖有3種實現(xiàn)算法,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ū)間,此時其他事務(wù)對該區(qū)間的操作都會被阻塞。

          間隙鎖是可重復(fù)讀RR隔離級別下特有的,另外還有幾種場景也會不使用間隙鎖。

          1. 事務(wù)隔離級別設(shè)置為讀已提交RC ,這樣肯定沒有間隙鎖了。

          2. Innodb_locks_unsafe_for_binlog設(shè)置為1

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

          而第三種Next-Key Lock實際上就是相當(dāng)于Record Lock+Gap Lock的組合。比如索引有10,20,30幾個值,那么被鎖住的區(qū)間可能會是(-∞,10],(10,20],(20,30],(30,+∞)。

          解決幻讀

          上一篇關(guān)于更新SQL執(zhí)行過程我們已經(jīng)對這個基礎(chǔ)有了一定的了解,在這里我們?nèi)サ艉瓦@里內(nèi)容無關(guān)的一些日志的細節(jié),把給數(shù)據(jù)加鎖的流程加入進去,這樣通過SQL執(zhí)行可以更好地理解Next-Key Lock到底是如何解決幻讀的,執(zhí)行過程如下:

          1. 首先第一步Server層會來查詢數(shù)據(jù)
          2. 存儲引擎根據(jù)查詢條件查到數(shù)據(jù)之后對數(shù)據(jù)進行加鎖,Record Lock或者間隙鎖,然后返回數(shù)據(jù)
          3. Server層拿到數(shù)據(jù)之后調(diào)用API去存儲引擎更新數(shù)據(jù)
          4. 最后存儲引擎返回結(jié)果,流程結(jié)束

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

          沒有索引

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

          因為city是沒有索引的,所以存儲引擎只能給所有的記錄都加上鎖,然后把數(shù)據(jù)都返回給Server層,然后Server層把city改成nanjing,再更新數(shù)據(jù)。

          因此,首先Record Lock會鎖住現(xiàn)有的7條記錄,間隙鎖則會對主鍵索引的間隙全部加上間隙鎖。

          所以,更新的時候沒有索引是非??膳碌囊患虑?,相當(dāng)于把整個表都給鎖了,那表都給鎖了當(dāng)然不存在幻讀了。

          普通索引

          我們再假設(shè)一個語句select * from user where age=20 for update。

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

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

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

          唯一&主鍵索引

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

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

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

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

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

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

          總結(jié)

          那最后說了這么多,RR級別下不是都已經(jīng)解決了幻讀的問題嗎,怎么還說有幻讀的問題呢?

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

          ·················END·················


          原來 Elasticsearch 還可以這么深入的理解


          一個搜索需求搞垮微服務(wù)


          微服務(wù)并不能解決你的爛代碼問題


          瀏覽 42
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  北条麻妃在线视频 | 韩国一区二区三区免费视频 | 国产成人一区二区三区 | 亚洲第一页欧美 | 操逼达人|