<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>

          面試官:幻讀是啥,會(huì)有什么問題?如何解決?

          共 3533字,需瀏覽 8分鐘

           ·

          2021-09-21 16:58

          你知道的越多,不知道的就越多,業(yè)余的像一棵小草!

          你來,我們一起精進(jìn)!你不來,我和你的競爭對手一起精進(jìn)!

          編輯:業(yè)余草

          cnblogs.com/jian0110/p/15080603.html

          推薦:https://www.xttblog.com/?p=5277

          大家中秋快樂!


          前言

          ?

          我們知道 MySQL 在可重復(fù)讀隔離級別下別的事物提交的內(nèi)容,是看不到的。而可提交隔離級別下是可以看到別的事務(wù)提交的。而如果我們的業(yè)務(wù)場景是在事物內(nèi)同樣的兩個(gè)查詢我們需要看到的數(shù)據(jù)都是一致的,不能被別的事物影響,就使用可重復(fù)讀隔離級別。這種情況下 RR 級別下的普通查詢(快照讀)依靠 MVCC 解決“幻讀”問題,如果是“當(dāng)前讀”的情況需要依靠什么解決“幻讀”問題呢?這就是本博文需要探討的。

          在探討前可以看下之前的博文(MySQL 是如何實(shí)現(xiàn)事務(wù)隔離?),主要介紹隔離級別的具體技術(shù)細(xì)節(jié),讀過以后看此篇文章可能更有幫助。

          注:本博文討論的“幻讀”都是指在“可重復(fù)讀”隔離級別下進(jìn)行。

          ?

          一、什么是幻讀?

          假設(shè)我們有表 t 結(jié)構(gòu)如下,里面的初始數(shù)據(jù)行為:(0,0,0),(1,1,1),(2,2,2),(3,3,3),(4,4,4),(5,5,5)。

          CREATE TABLE `t`
          (
              `id` INT(11) NOT NULL,
              `key`  INT(11) DEFAULT NULL,
              `value`  INT(11) DEFAULT NULL,
              PRIMARY KEY (`id`),
              KEY `value` (`value`)
          ) ENGINE 
          = InnoDB;
          INSERT INTO t
          VALUES (000),
                 (111),
                 (222),
                 (333),
                 (444),
                 (555)

          假設(shè)select * from where value=1 for update,只在這一行加鎖(注意這只是假設(shè)),其它行不加鎖,那么就會(huì)出現(xiàn)如下場景:

          Session A 的三次查詢 Q1-Q3 都是select * from where value = 1 for update,查詢的 value=1 的所有 row。

          • T1:Q1只返回一行(1,1,1);
          • T2:session B 更新 id=0 的 value 為 1,此時(shí)表 t 中 value=1 的數(shù)據(jù)有兩行
          • T3:Q3 返回兩行(0,0,1),(1,1,1)
          • T4:session C插入一行(6,6,1),此時(shí)表 t 中 value=1 的數(shù)據(jù)有三行
          • T5:Q3 返回三行(0,0,1),(1,1,1),(6,6,1)
          • T6:session A 事物 commit。

          其中 Q3 讀到 value=1 這一樣的現(xiàn)象,就稱之為幻讀,「幻讀指的是一個(gè)事務(wù)在前后兩次查詢同一個(gè)范圍的時(shí)候,后一次查詢看到了前一次查詢沒有看到的行」。

          先對“幻讀”做出如下解釋:

          • 「在可重復(fù)讀隔離級別下,普通的查詢是快照讀,是不會(huì)看到別的事務(wù)插入的數(shù)據(jù)的。因此,幻讀在“當(dāng)前讀”下才會(huì)出現(xiàn)(三個(gè)查詢都是for update表示當(dāng)前讀);」
          • 「上面 session B 的修改 update 結(jié)果,被 session A 之后的 select 語句用“當(dāng)前讀”看到,不能稱為幻讀,幻讀僅專指“新插入的行”」

          幻讀有什么問題?

          「(1)需要單獨(dú)解決」

          眾所周知,select ...for update語句就是將相應(yīng)的數(shù)據(jù)行鎖住,比如 session A 在 T1 時(shí)刻的 Q1 查詢語句:select * from where value=1 for update就是將 value=1 的數(shù)據(jù)行鎖住,但顯然如果是上述的場景發(fā)生,此時(shí)的 for update 語義被破壞了(并沒有鎖住 value=1 的數(shù)據(jù)行)。

          「即使把所有的記錄都加上鎖,還是阻止不了新插入的記錄,所以“幻讀”問題要單獨(dú)拿出來解決。沒法依靠 MVCC 或者行鎖機(jī)制來解決。這就引出“間隙鎖”,是另外一種加鎖機(jī)制」

          「(2)間隙鎖引發(fā)的并發(fā)度」

          間隙鎖引入以后,可能會(huì)導(dǎo)致同樣語句鎖住更大的范圍,這可能就會(huì)影響了并發(fā)度。具體請看下面介紹

          「三、如何解決幻讀?」

          「產(chǎn)生幻讀的原因是,行鎖只能鎖住行,但是新插入記錄這個(gè)動(dòng)作,要更新的是記錄之間的“間隙”。因此,為了解決幻讀問題,InnoDB 只好引入新的鎖,也就是間隙鎖(Gap Lock)」

          間隙:比如表中加入 6 個(gè)記錄0,5,10,15,20,25。則產(chǎn)生 7 個(gè)間隙:

          產(chǎn)生 7 個(gè)間隙

          「在一行行掃描的過程中,不僅將給行加上了行鎖,還給行兩邊的空隙也加上了間隙鎖。這樣就確保了無法再插入新的記錄。」

          「間隙鎖和行鎖合稱 next-key lock,每個(gè) next-key lock 是前開后閉區(qū)間(間隙鎖開區(qū)間,next-key lock 前開后閉區(qū)間)」

          間隙鎖與間隙鎖之間是不存在沖突的,沖突的是往間隙里插入一條記錄。

          間隙鎖與間隙鎖之間是不存在沖突

          表 t 中是沒有 value=7 這個(gè)數(shù)據(jù)的,所以 Q1 加的間隙鎖(1,5),而 Q2 也是加的這個(gè)間隙鎖,兩者不沖突都是為了保護(hù)這個(gè)間隙不允許插入值。

          在表 t 初始化后,假設(shè)表的數(shù)據(jù)如下:

          間隙不允許插入值

          如果用select * from for update執(zhí)行,則會(huì)把整個(gè)表所有記錄鎖起來,就形成了 7 個(gè) next-key lock,分別是(-∞,0]、(0,2]、(2,4]、(4,6]、(6,8]、(8, 10]、(10, +supremum]。

          間隙鎖的引入,可能會(huì)導(dǎo)致同樣的語句鎖住更大的范圍,是會(huì)影響了并發(fā)度

          假設(shè)發(fā)生如下場景:

          發(fā)生了死鎖

          則明顯發(fā)生了死鎖,分析如下:

          • Q1:執(zhí)行select …for update語句,由于id=9這一行并不存在,因此會(huì)加上間隙鎖 (8,10);

          • Q2:執(zhí)行select …for update語句,同樣會(huì)加上間隙鎖 (8,10),間隙鎖之間不會(huì)沖突,因 此這個(gè)語句可以執(zhí)行成功;

          • session B 試圖插入一行 (9,9,9),被 session A 的間隙鎖擋住了,只好進(jìn)入等待;

          • session A 試圖插入一行 (9,9,9),被 session B 的間隙鎖擋住了。

          有上述可知間隙鎖的引入,可能會(huì)導(dǎo)致同樣語句鎖住更大的范圍,這其實(shí)是影響了并發(fā)度。

          為了解決幻讀問題可以采用讀可提交隔離級別,間隙鎖是在可重復(fù)讀隔離級別下才會(huì)生效的。所以如果把隔離級別設(shè)置為讀提交的話, 就沒有間隙鎖了。但同時(shí),你要解決可能出現(xiàn)的數(shù)據(jù)和日志不一致問題,需要把 binlog 格式設(shè)置為 row,也就是說采用“ RC 隔離級別 + 日志格式 binlog_format=row ”組合。

          總結(jié)

          • 「RR 隔離級別下間隙鎖才有效,RC 隔離級別下沒有間隙鎖;」

          • 「RR 隔離級別下為了解決“幻讀”問題:“快照讀”依靠 MVCC 控制,“當(dāng)前讀”通過間隙鎖解決;」

          • 「間隙鎖和行鎖合稱 next-key lock,每個(gè) next-key lock 是前開后閉區(qū)間;」

          • 「間隙鎖的引入,可能會(huì)導(dǎo)致同樣語句鎖住更大的范圍,影響并發(fā)度?!?/strong>

          瀏覽 132
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <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>
                  2023AV天堂网 | 日韩一级黄色 | 青青草视频在线免费观看 | 国产精品无码成人网站视频 | 国语对白在线免费视频 |