<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                ## 前言 InnoDB并發過程中使用兩類鎖進行同步。 1\. 事務鎖 維護在不同的Isolation level下數據庫的Atomicity和Consistency兩大基本特性。 InnoDB定義了如下的lock mode: ~~~ /* Basic lock modes */ enum lock_mode { LOCK_IS = 0, /* intention shared */ LOCK_IX, /* intention exclusive */ LOCK_S, /* shared */ LOCK_X, /* exclusive */ LOCK_AUTO_INC, /* locks the auto-inc counter of a table in an exclusive mode */ LOCK_NONE, /* this is used elsewhere to note consistent read */ LOCK_NUM = LOCK_NONE, /* number of lock modes */ LOCK_NONE_UNSET = 255 }; ~~~ 2\. 內存鎖 為了維護內存結構的一致性,比如Dictionary cache、sync array、trx system等結構。 InnoDB并沒有直接使用glibc提供的庫,而是自己封裝了兩類: 1\. 一類是mutex,實現內存結構的串行化訪問 2\. 一類是rw lock,實現讀寫阻塞,讀讀并發的訪問的讀寫鎖 讀者如果有興趣,可以直接翻閱InnoDB的代碼,這里我們主要介紹index lock所使用的rw lock。 ## InnoDB index lock InnoDB默認使用B-Tree結構來保存數據,如下圖所示的B-Tree結構: ![InnoDB B-Tree結構](https://box.kancloud.cn/2015-09-24_56039b2f4ba52.png "InnoDB B-Tree") 這個B-Tree一共有兩類節點,一類是node(branch) block,一類是leaf block,對于內存中的每一個block,都有一個rw lock與之相對應,用于保護block內部結構的一致性,阻塞并發修改。每一個index在內存中保持著一個index字典對象,即`dict_index_t`,并對應著一個index lock,同樣屬于rw lock類型,用于保護B-Tree的平衡樹結構。 所以,InnoDB為每一個index,維護兩種rw lock: 1\. index級別的,用于保護B-Tree結構不被破壞 2\. block級別的,用于保護block內部結構不被破壞 很明顯,rw lock 鎖保護的對象的級別越高,沖突的可能性就越大,并發的瓶頸也就越容易出現。 ## InnoDB index lock的處理場景分析 1. 我們先來看rw lock的模型,rw lock一共使用兩類lock mode,即S鎖和X鎖,其相容性矩陣是: ~~~ | S| X| --+--+--+ S | o| x| --+--+--+ X | x| x| --+--+--+ ~~~ 按照lock mode,數據庫對B-Tree操作區分幾種類型: ~~~ btr_search_leaf btr_modify_leaf btr_modify_tree btr_search_prev btr_modify_prev ~~~ 根據這些不同的操作類型,我們下面來分析一下加鎖的過程。 ## 場景分析 ### 場景1\. 索引掃描查詢 如果sql通過索引進行掃描,其latch mode為`btr_search_leaf`: 首先是hold住index lock的RW_S_LATCH,然后通過`btr_cur_search_to_nth_level`進行B-Tree查詢leaf節點的過程。當cursor定位到leaf節點上之后,在leaf page節點上,添加RW_S_LATCH鎖,即S鎖,然后通過save_point的mtr釋放index lock的S鎖。在掃描的過程中,因為持有index的RW_S_LATCH,所以節點的掃描比如root、branch這樣的node block,并不持有任何mode的rw lock。直到latch住leaf節點后,就釋放掉 index 的鎖,這樣盡可能的減少阻塞,剩下就是leaf節點的掃描過程,只持有leaf page的鎖。 掃描完數據,就釋放leaf page的S鎖。 ### 場景2\. 升序和降序查詢 場景2和場景1在持有index lock的過程中,是相同的,都是在search的過程中,持有RW_S_LATCH,一旦定位到leaf page,就釋放掉index 的S鎖,升序和降序的掃描過程中,會沿著leaf page之間的雙鏈表進行掃描,因為是雙向鏈表,所以可以完成asc和desc的掃描。但這里要注意的是,InnoDB先持有下一個page的lock,然后再釋放當前持有page的lock,這樣就有可能造成死鎖,所以InnoDB不管當前是asc還是desc的掃描,都會先持有左leaf page的lock,然后再持有下一個leaf page的鎖,最后釋放prev page的lock,這樣做到加鎖的順序一致,避免死鎖。 ### 場景3\. 樂觀插入記錄 InnoDB在插入記錄的過程中,分了兩個步驟,樂觀插入和悲觀插入: 1\. 樂觀,就是當前leaf page的剩余空間滿足記錄的插入需要; 2\. 悲觀,就是需要split B-Tree,增加leaf page來完成新記錄的插入。 先看樂觀插入: 場景1和場景2都持有leaf page的RW_S_LATCH,但在插入的過程中,操作類型是btr_modify_leaf,需要持有leaf page的RW_X_LATCH, 在search的過程中,和場景1、2相同,都是持有index的RW_S_LATCH lock,一旦定位結束,釋放index lock。 ### 場景4\. 悲觀插入記錄 悲觀插入,需要split B-Tree,所以首先會持有index lock,mode為RW_X_LATCH,并X lock三個leaf page,即prev,current,next三個leaf page,然后修改branch節點的記錄,指向leaf節點,修改完成后,才能釋放index lock。 在split的過程中,無法進行search操作(因為正在修改branch節點),但如果其他線程已經在讀取leaf page,并不會受影響。 ### 場景5\. online DDL 在online DDL的過程中,比如add index,因為是新添加的index,并不會產生并發訪問的問題。 ### 場景6\. DDL 比如加字段的過程,其并發問題,由server層的MDL鎖和InnoDB層的事務鎖來完成其同步。 ## 問題: 我們來看上面提到的6個場景,對我們日常使用InnoDB的過程中,影響最大的就是場景4,即split的過程中,會嚴重的影響并發,因為index 的X lock,導致任何的B-Tree掃描都產生了阻塞。有解嗎? 通常我們碰到lock導致的并發問題的時候,第一個想到的就是降低鎖對象的粒度,粒度越小,共享區域也就越小,沖突的幾率也就越小,并發就能夠提高。 根據這個原則,我們回過頭來看這個問題,因為index lock 保護了整個B-Tree的結構,但我們對某一個branch節點進行split的時候,我們僅僅修改了這個branch節點,所以我們可以把鎖的粒度降低到某些要修改的branch節點上,這樣就可以不影響其他branch節點的掃描和訪問。 ## MySQL 5.7的改進 MySQL官方對index lock進行了優化,在split的過程中,盡可能的減少沖突,減少并發的瓶頸。 對于InnoDB的rw lock增加第三種lock mode,即SX鎖,其相容性矩陣如下: ~~~ | S|SX| X| --+--+--+--+ S | o| o| x| --+--+--+--+ SX| o| x| x| --+--+--+--+ X | x| x| x| --+--+--+--+ ~~~ 這里仍然保留了index lock,考慮一下兩個存在沖突的場景,還是否阻塞: 1\. BTR_SEARCH_LEAF和BTR_MODIFY_LEAF 對于掃描leaf節點和修改leaf節點的場景: ~~~ index->lock 持有S鎖不變 branch->latch 從無--> S latch latch order: latch root block (S) latch root-1 block (S) .... latch leaf+1 block (S) leaf->latch 持有S或者X鎖不變 release index lock 不變 release branch latch 從無到釋放 ~~~ 和之前的差別是在search的過程中,對使用到的branch節點,加上S鎖,用于同步branch節點的修改。同樣,當定位到leaf節點后,就可以把index lock和branch lock全部釋放掉了,后面leaf節點之間的移動,同樣不需要index lock和branch lock。 2\. BTR_MODIFY_TREE 對于修改index B-Tree結構的場景: ~~~ index->lock 從X鎖-->SX 鎖 branch->latch 從無--> X latch ~~~ 注意:因為有index SX鎖,所以不允許并發的修改B-Tree操作,所以,只需要X latch要修改的branch即可。 和之前的差別就是index lock從X鎖變成了SX鎖,這樣并不影響search的過程,增加了更改過程中branch節點的X鎖。 ## 總結: 這樣修改后,index lock在并發的過程中,修改B-Tree和search B-Tree沒有了并發沖突問題,在split的過程中,只有search和modify到同一個branch節點,才會產生阻塞,對于我們正常的使用數據庫過程中(大部分都是通過index進行讀寫),可以顯著的提升并發能力。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看