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

          靈魂發(fā)問(wèn):MySQL是如何解決幻讀的?

          共 5630字,需瀏覽 12分鐘

           ·

          2022-08-04 11:50

          程序員的成長(zhǎng)之路
          互聯(lián)網(wǎng)/程序員/技術(shù)/資料共享 
          關(guān)注


          閱讀本文大概需要 5 分鐘。

          來(lái)自:blog.csdn.net/sinat_27143551/article/details/81736330

          概念

          MySQL InnoDB支持三種行鎖定方式:

          • 行鎖(Record Lock):鎖直接加在索引記錄上面。
          • 間隙鎖(Gap Lock):鎖加在不存在的空閑空間,可以是兩個(gè)索引記錄之間,也可能是第一個(gè)索引記錄之前或最后一個(gè)索引之后的空間。
          • Next-Key Lock:行鎖與間隙鎖組合起來(lái)用就叫做Next-Key Lock。

          當(dāng)我們用范圍條件而不是相等條件檢索數(shù)據(jù),并請(qǐng)求共享或排他鎖時(shí),InnoDB會(huì)給符合條件的已有數(shù)據(jù)記錄的索引項(xiàng)加鎖;對(duì)于鍵值在條件范圍內(nèi)但并不存在的記錄,叫做“間隙(GAP)”,InnoDB也會(huì)對(duì)這個(gè)“間隙”加鎖,這種鎖機(jī)制就是所謂的間隙鎖(Next-Key鎖)。

          舉例來(lái)說(shuō),假如user表中只有101條記錄,其empid的值分別是 1,2,...,100,101,下面的SQL:

          select * from  user where user_id > 100 for update;

          是一個(gè)范圍條件的檢索,InnoDB不僅會(huì)對(duì)符合條件的user_id值為101的記錄加鎖,也會(huì)對(duì)user_id大于101(這些記錄并不存在)的“間隙”加鎖。

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

          InnoDB使用間隙鎖的目的,一方面是為了防止幻讀,以滿(mǎn)足相關(guān)隔離級(jí)別的要求,對(duì)于上面的例子,要是不使用間隙鎖,如果其他事務(wù)插入了user_id大于100的任何記錄,那么本事務(wù)如果再次執(zhí)行上述語(yǔ)句,就會(huì)發(fā)生幻讀;另外一方面,是為了滿(mǎn)足其恢復(fù)和復(fù)制的需要

          快照讀和當(dāng)前讀

          快照讀歷史數(shù)據(jù)-mvcc

          innodb的默認(rèn)事務(wù)隔離級(jí)別是rr(可重復(fù)讀)。它的實(shí)現(xiàn)技術(shù)是mvcc(MVCC只在讀提交可重復(fù)讀兩種隔離級(jí)別下工作)。基于版本的控制協(xié)議。該技術(shù)不僅可以保證innodb的可重復(fù)讀,而且可以防止幻讀。但是它防止的是快照讀,也就是讀取的數(shù)據(jù)雖然是一致的,但是數(shù)據(jù)是歷史數(shù)據(jù)。

          當(dāng)前讀最新數(shù)據(jù)-next-key lock

          如何做到保證數(shù)據(jù)是一致的(也就是一個(gè)事務(wù),其內(nèi)部讀取對(duì)應(yīng)某一個(gè)數(shù)據(jù)的時(shí)候,數(shù)據(jù)都是一樣的),同時(shí)讀取的數(shù)據(jù)是最新的數(shù)據(jù)。innodb提供了next-key lock,也就是結(jié)合gap鎖與行鎖,達(dá)到最終目的。

          實(shí)現(xiàn):

          1.快照讀(snapshot read)

          簡(jiǎn)單的select操作(不包括 select ... lock in share mode, select ... for update)

          2.當(dāng)前讀(current read)

          select ... lock in share mode、select ... for update

          insert、update、delete

          在RR級(jí)別下,快照讀是通過(guò)MVCC(多版本控制)和undo log來(lái)實(shí)現(xiàn)的,當(dāng)前讀是通過(guò)加record lock(記錄鎖)和gap lock(間隙鎖)來(lái)實(shí)現(xiàn)的。

          測(cè)試

          建表innodb_lock:

          DROP TABLE IF EXISTS `innodb_lock`;
          CREATE TABLE `innodb_lock` (
            `a` int(10NOT NULL,
            `b` varchar(255NOT NULL DEFAULT '',
            KEY `index_a` (`a`),
            KEY `index_b` (`b`)
          ENGINE=InnoDB;

          插入數(shù)據(jù),注意這里邊沒(méi)有a為2的數(shù)據(jù):

          INSERT INTO `innodb_lock` VALUES ('1''b2');
          INSERT INTO `innodb_lock` VALUES ('3''3');
          INSERT INTO `innodb_lock` VALUES ('4''4000');
          INSERT INTO `innodb_lock` VALUES ('5''5000');
          INSERT INTO `innodb_lock` VALUES ('6''6000');
          INSERT INTO `innodb_lock` VALUES ('7''7000');
          INSERT INTO `innodb_lock` VALUES ('8''8000');
          INSERT INTO `innodb_lock` VALUES ('9''9000');

          (1)開(kāi)啟兩個(gè)客戶(hù)端,修改事務(wù)隔離級(jí)別為可重復(fù)讀

          (2)開(kāi)啟事務(wù),在左側(cè)客戶(hù)端批量修改a為1~6范圍內(nèi)的數(shù)據(jù)。在右側(cè)客戶(hù)端插入a為2的數(shù)據(jù)。右側(cè)操作被阻塞。說(shuō)明有間隙鎖。

          (3)重復(fù)(2),事務(wù)隔離級(jí)別依然是repeatable read,只不過(guò)變成在右側(cè)客戶(hù)端插入a為10的數(shù)據(jù),成功。

          (4)事務(wù)隔離級(jí)別設(shè)置為read committed,重復(fù)步驟(2),發(fā)現(xiàn)右側(cè)客戶(hù)端的操作成功,說(shuō)明該隔離級(jí)別無(wú)間隙鎖。

          (5)還要特別說(shuō)明的是,InnoDB除了通過(guò)范圍條件加鎖時(shí)使用間隙鎖外,如果使用相等條件請(qǐng)求給一個(gè)不存在的記錄加鎖,InnoDB也會(huì)使用間隙鎖!左側(cè)客戶(hù)端給不存在的記錄加鎖,右側(cè)客戶(hù)端的增加操作阻塞。

          但是,如果a是唯一索引,不會(huì)升級(jí)全表鎖。

          先添加唯一索引:

          (6)重復(fù)步驟(5),發(fā)現(xiàn)右側(cè)客戶(hù)端不會(huì)被阻塞,數(shù)據(jù)插入成功



          小結(jié)

          很顯然,在使用范圍條件檢索并鎖定記錄時(shí),InnoDB這種加鎖機(jī)制會(huì)阻塞符合條件范圍內(nèi)鍵值的并發(fā)插入,這往往會(huì)造成嚴(yán)重的鎖等待。因此,在實(shí)際應(yīng)用開(kāi)發(fā)中,尤其是并發(fā)插入比較多的應(yīng)用,我們要盡量?jī)?yōu)化業(yè)務(wù)邏輯,盡量使用相等條件來(lái)訪問(wèn)更新數(shù)據(jù),避免使用范圍條件;當(dāng)然,對(duì)一條不存在的記錄加鎖,也會(huì)有間隙鎖的問(wèn)題。
          間隙鎖在InnoDB的唯一作用就是防止其它事務(wù)的插入操作,以此來(lái)達(dá)到防止幻讀的發(fā)生,所以間隙鎖不分什么共享鎖與排它鎖。如果InnoDB掃描的是一個(gè)主鍵、或是一個(gè)唯一索引的話,那InnoDB只會(huì)采用行鎖方式來(lái)加鎖,而不會(huì)使用Next-Key Lock的方式,也就是說(shuō)不會(huì)對(duì)索引之間的間隙加鎖,仔細(xì)想想的話,這個(gè)并不難理解,大家也可以自己測(cè)試一下。
          要禁止間隙鎖的話,可以把隔離級(jí)別降為讀已提交,或者開(kāi)啟參數(shù)innodb_locks_unsafe_for_binlog。

          補(bǔ)充

          MySQL InnoDB的可重復(fù)讀并不保證避免幻讀,需要應(yīng)用使用加鎖讀來(lái)保證。而這個(gè)加鎖度使用到的機(jī)制就是next-key locks
          本事務(wù)中第一次讀取出一行,做了一次更新后,另一個(gè)事務(wù)里提交的數(shù)據(jù)就出現(xiàn)了,也可以看做是一種幻讀:
          t Session A                 Session B
          |
          START TRANSACTION;        START TRANSACTION;
          |
          SELECT * FROM innodb_lock;
          | +------+-------+
          | | a    | b    |
          | +------+-------+
          | |    1 | a    |
          | +------+-------+
          |                           INSERT INTO innodb_lock
          |                           VALUES (2'b');
          |
          SELECT * FROM innodb_lock;
          | +------+-------+
          | | a    | b    |
          | +------+-------+
          | |    1 | a    |
          | +------+-------+
          |                           COMMIT;
          |
          SELECT * FROM innodb_lock;
          | +------+-------+
          | | a    | b    |
          | +------+-------+
          | |    1 | a    |
          | +------+-------+
          |
          UPDATE innodb_lock SET b='z';
          | Rows matched: 2  Changed: 2 Warnings: 0
          | (怎么多出來(lái)一行)
          |
          SELECT * FROM innodb_lock;
          | +------+-------+
          | | a    | b    |
          | +------+-------+
          | |    1 | z    |
          | |    2 | z    |
          | +------+-------+
          |
          <END>

          推薦閱讀:

          扔掉工具類(lèi)!MyBatis 一個(gè)簡(jiǎn)單配置搞定加密、解密,不能太方便了~!

          實(shí)現(xiàn)一個(gè)小輪子—用AOP實(shí)現(xiàn)異步上傳

          互聯(lián)網(wǎng)初中高級(jí)大廠面試題(9個(gè)G)

          內(nèi)容包含Java基礎(chǔ)、JavaWeb、MySQL性能優(yōu)化、JVM、鎖、百萬(wàn)并發(fā)、消息隊(duì)列、高性能緩存、反射、Spring全家桶原理、微服務(wù)、Zookeeper......等技術(shù)棧!

          ?戳閱讀原文領(lǐng)?。?/span>                                  朕已閱 

          瀏覽 51
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

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

          手機(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>
                  午夜看黄 | 橾逼网站 | 精品欧美色视频网站在线观看 | 操老女人视频 | 性爱综合网 |