1. 程式人生 > >讀書筆記-MySQL運維內參08-索引實現原理2

讀書筆記-MySQL運維內參08-索引實現原理2

自己 新節點 .cn 記錄 產生 連接 -m 父節點 alt

我們已經知道B+樹的組織結構及不同層之間是如何關聯的了。

現在我們模擬一個B+樹是如何從小到大,從無到有,從簡到繁的過程。

首先我們來做一些假設:

1,每個頁面包括內節點和葉子節點最多可以插入三條記錄,插入第四條的時候,就會導致分裂。

2,插入的數據是鍵值對,但是我們只關註鍵,值可以不用關註,就簡單的以data表示。

3,插入數據序列為:10,20,5,8,23,22,50,21,53,40,9

4,為了簡明一些,key就是一些簡單的int類型的數字

5,假設根節點的頁面號是100

第一次插入過程。此時,索引中沒有數據,所以B+樹是一個空的根節點。

因為頁面只能存儲三個Key,首先將10,20,5插入進去,然後在頁面內做數據庫排序,索引三個key插入以後,B+樹應該是下圖的樣子。

技術分享

根據假設,跟頁面已經插入滿了,但是還有更多的數據需要插入,如果繼續插入,則需要跟面葉分裂,分類過程如下:

1,首先創建一個新的葉子節點,假設申請出來的額頁面是101號。在分裂過程中,根節點始終是不會變得,不管變成多大的樹,根節點的頁面始終如一。

2,然後將根節點的全部記錄復制到新的頁面中,原根頁面的最小記錄指向新的葉子節點,同時將根頁面的記錄全部刪除。

3,最後將根頁面的min指針指向新的葉子節點101號頁面。

這樣變換以後,新的B+樹結構入下圖所示

技術分享

此時,根節點的分類完成了,但是我們要插入的key--8還沒有插入進去,所以還需要繼續插入。

插入8 的時候,可以想到,通過定位直接回找到101號頁面,在這個頁面內插入時,發現還是沒有空間,此時這樣頁面屬於葉子節點,所以有涉及到了一次葉子節點的分裂。

步驟如下:

1,首先創建一個葉子節點,假設頁面號未102

2,將101號上的一半的數據遷移到102號頁面,這裏可以假設每次遷移過去1條

3,101號頁面和102號頁面都是葉子節點,一般稱為兄弟關系,他們需要組成雙向鏈表。

4,將一半的數據遷移到102號頁面之後,102號頁面只有key為20的這1條記錄。註意102號頁面也是葉子節點,那麽這個頁面也需要和根節點掛上關系。需要做的就是將20這條記錄的key取出來,

  然後再加上一個指針信息,就是這裏的102號頁面,組成一條新的記錄插入到根頁面中,那麽這條記錄就算指向對應的兒子節點102號頁面了。

這樣,葉子節點分裂並重新調整之後的B+樹,如下圖所示:

技術分享

分裂到現在為止,插入key(8)就非常簡單了。

從跟頁面開始搜索,因為8比20小,所以還是從min這個記錄找對應的葉子節點,就找到了101號頁面。然後在這個頁面上做插入操作,這次已經滿足插入條件了。

同樣的方法,從根節點開始搜索,可以簡單了插入key(23),key(22)這兩行記錄。此時的B+如下圖所示:

技術分享

此時繼續插入key(50)這行數據,因為它是大於20的,索引查到了102子頁面,但是102頁面已經滿了,那就繼續分裂吧。這個分類依然是葉子節點的分裂。

同樣先申請一個葉子節點頁面,設頁面號是103號頁面。然後將102號頁面上的一半數據key(23)移動到103號頁面,再講103號頁面和根頁面產生父子關系。

這樣就產生了一個新的葉子節點103號頁面。103號頁面也是102號頁面的兄弟節點,需要用雙向鏈表連接起來。

這樣變換之後,新的B+樹結構入下圖:

技術分享

分裂到此之後,用同樣的方法,從根節點開始搜索,可以方便的插入key(50),key(21),key(53)三行數據。

那麽繼續插入key(40)的時候,因為它比key(23)大,所以應該插入到103號頁面,,但103號頁面已經滿了,所以再次發生葉子節點的分裂。

假設這次申請到的頁面是104號頁面,分裂並插入key(40)之後的B+樹如下圖:

技術分享

繼續插入下一數據key(9)。在根頁面中,因為key(9)比key(20)小,所以應該找到101號頁面插入數據,但是該頁面已經儲存滿了,需要再次頁面分裂。

同樣的創建一個新的葉子節點頁面,設頁面號是105。移動數據,使其成為101號頁面的兄弟節點。

都做完之後,將105號頁面上唯一的一個key(10)(從101號頁面移動過來的一條)與根節點產生父子關系,創建一條新的索引記錄(Key:10,Pageno:105),並且插入到根節點頁面中。

此時發現跟頁面已經滿了,很明顯需要做根頁面的分裂了。

我們首先李看一下目前的B+樹(其實不能稱之為一個完整的B+樹)如下圖所示:

技術分享

可以看到105號頁面是沒有父親的,需要通過分裂為他找到一個父親。

新申請的頁面應該是內節點頁面,因為要存儲的是索引,而不是葉子頁面的數據。假設這個頁面是106號。

這裏需要註意,由於根節點始終是跟節點,因此還是需要將根節點上的所有數據移動到106號頁面上,相當於從101到105這5個頁面的父節點變成106號了。而100號頁面的最小節點也會指向106號頁面。

此時的B+樹已經是3層結構了,如下圖所示:

技術分享

此時的105號節點,依然不能再父節點寫入索引信息,因為106號依然是滿了。那就繼續分裂吧。

指示這次測分裂不是頁面節點也不是根節點,而是內節點的分裂。

內節點的分裂和葉子節點的分裂方法差不多,依然是申請一個新節點,假設是107號頁面,然後作為106號頁面的兄弟頁面,移動一條數據到107號頁面之後,將索引記錄(key:53,pageno:107)插入到根頁面中。

此時,新的B+樹結構如下圖所示:

技術分享

這時,繼續在106號頁面插入索引數據(key:10,pageno:105)。此時,經過兩次分裂,

此時的106號頁面包括key(10,20,23)三條記錄了,分別指向105,102,103號頁面。

終於105號也找到了自己的父節點,此時的B+樹入下圖所示:

技術分享

等等,別忘了正事,我們的數據插入的任務還沒有完成呢。

插入數據key(9)。從根開始搜索,依然會找到101號頁面,此時可以直接把數據寫到101號頁面了。

將所有數據插入完成之後的完整的B+樹入下圖所示:

技術分享

此時,我們回頭看一下,要插入的數據的序列是:10,20,5,23,22,50,21,53,40,9。

此時可以看下最後這張圖的葉子節點,從左到右把所有的記錄都讀取出來,序列為:5,8,9,10,20,21,22,23,40,50,53。

很明顯的可以看到,相當於給原始序列做了一次排序,並且包含了全部數據。

若想要查詢全表的話,在InnoDB內部,會直接定位到最左邊的葉子節點上,然後從左到右將所有數據讀取出來,而這也正式我們所介紹的B+樹的特性。

讀書筆記-MySQL運維內參08-索引實現原理2