1. 程式人生 > >淺談B樹,B+樹,B*樹及分析MySQL的索引

淺談B樹,B+樹,B*樹及分析MySQL的索引

樹的基本概念

:樹的頂端結點
兄弟:具有同一個雙親(Parent)的孩子(Child)之間互稱為兄弟(Sibling)。
祖先:結點的祖先(Ancestor)是從根(Root)到該結點所經分支(Branch)上的所有結點。
葉子(終端結點):沒有孩子的結點(也就是度為0的結點)稱為葉子(Leaf)或終端結點。
:結點所擁有的子樹個數稱為結點的度(Degree)。
:一個結點和另一個結點之間的連線被稱之為邊(Edge)。
層次:結點的層次(Level)從根(Root)開始定義起,根為第0層,根的孩子為第1層。
路徑:連線結點和其後代的結點之間的(結點,邊)的序列。
結點的高度:結點的高度是該結點和某個葉子之間存在的最長路徑上的邊的個數。
樹的高度

:樹的高度是其根結點的高度。
結點的深度:結點的深度是從樹的根結點到該結點的邊的個數。(注:樹的深度指的是樹中結點的最大層次。)
森林:森林是n(>=0)棵互不相交的樹的集合。

二叉樹(Binary Search Tree)

定義:每個結點至多擁有兩棵子樹(即二叉樹中不存在度大於2的結點),並且,二叉樹的子樹有左右之分,其次序不能任意顛倒。
二叉樹的性質

  1. 若二叉樹的層次從0開始,則在二叉樹的第i層至多有2^i個結點(i>=0)。
  2. 高度為k的二叉樹最多有2^(k+1) - 1個結點(k>=-1)。 (空樹的高度為-1)高度為k的二叉樹最多有2^(k+1) - 1個結點(k>=-1)。 (空樹的高度為-1)
  3. 對任何一棵二叉樹,如果其葉子結點(度為0)數為m, 度為2的結點數為n, 則m = n + 1。對任何一棵二叉樹,如果其葉子結點(度為0)數為m, 度為2的結點數為n, 則m = n + 1。

B樹

B樹的結構要求:
1)根節點至少有兩個子節點
2)每個節點有M-1個key,並且以升序排列
3)位於M-1和M key的子節點的值位於M-1 和M key對應的Value之間
4)其它節點至少有M/2個子節點
5)所有葉子節點都在同一層
在這裡插入圖片描述

查詢
  以上圖為例:若查詢的數值為5:
  第一次磁碟IO:在記憶體中定位(與17、35比較),比17小,左子樹;
  第二次磁碟IO:在記憶體中定位(與8、12比較),比8小,左子樹;
  第三次磁碟IO:在記憶體中定位(與3、5比較),找到5,終止。
整個過程中:比較的次數並不比二叉查詢樹少,尤其適當某一節點中的資料很多時,但是磁碟IO的次數卻是大大減少。比較是在記憶體中進行的,相比於磁碟IO的速度,比較的耗時幾乎可以忽略。所以當樹的高度足夠低的話,就可以極大的提高效率。相比之下,節點中的元素多點也沒關係,僅僅是多了幾次記憶體互動而已,只要不超過磁碟頁的大小即可。

B+樹

B+樹是B樹的升級版本,有著比B樹更高的查詢效率,也是一種多路搜尋樹:

  1. 其定義基本與B-樹同,除了:
  2. 非葉子結點的子樹指標與關鍵字個數相同;
  3. 非葉子結點的子樹指標P[i],指向關鍵字值屬於[K[i], K[i+1])的子樹(B-樹是開區間);
  4. 為所有葉子結點增加一個鏈指標;
  5. 所有關鍵字都在葉子結點出現;
    在這裡插入圖片描述

查詢
首先,B+樹的查詢和B樹一樣,類似於二叉查詢樹。起始於根節點,自頂向下遍歷樹,選擇其分離值在要查詢值的任意一邊的子指標。在節點內部典型的使用是二分查詢來確定這個位置。
不同的是:

  1. B+樹中間節點沒有衛星資料(索引元素所指向的資料記錄),只有索引,而B樹每個結點中的每個關鍵字都有衛星資料;這就意味著同樣的大小的磁碟頁可以容納更多節點元素,在相同的資料量下,B+樹更加“矮胖”,IO操作更少。
    B樹的衛星資料:
    在這裡插入圖片描述
    B+樹的衛星資料:
    在這裡插入圖片描述
    需要補充的是,在資料庫的聚集索引(Clustered Index)中,葉子節點直接包含衛星資料。在非聚集索引(NonClustered Index)中,葉子節點帶有指向衛星資料的指標。
  2. 因為衛星資料的不同,導致查詢過程也不同;B樹的查詢只需找到匹配元素即可,最好情況下查詢到根節點,最壞情況下查詢到葉子結點,所說效能很不穩定,而B+樹每次必須查詢到葉子結點,效能穩定
  3. 在範圍查詢方面,B+樹的優勢更加明顯:B樹的範圍查詢需要不斷依賴中序遍歷。首先二分查詢到範圍下限,在不斷通過中序遍歷,知道查詢到範圍的上限即可。整個過程比較耗時。
      而B+樹的範圍查詢則簡單了許多。首先通過二分查詢,找到範圍下限,然後同過葉子結點的連結串列順序遍歷,直至找到上限即可,整個過程簡單許多,效率也比較高。
      例如:同樣查詢範圍[3-11],兩者的查詢過程如下:
      B樹的查詢過程:
      在這裡插入圖片描述
      B+樹的查詢過程:
      在這裡插入圖片描述

B*樹

接下來要說明的就是B樹,B樹是對B+樹進行的又一次的升級。在B+樹的非根和非葉子結點再增加指向兄弟的指標;
在這裡插入圖片描述
在B+樹基礎上,為非葉子結點也增加連結串列指標,將結點的最低利用率從1/2提高到2/3;
在這比如說當你進行插入節點的時候,它首先是放到兄弟節點裡面。如果兄弟節點滿了的話,進行分裂的時候從兄弟節點和這個節點各取出1/3,放入新建的節點當中,這樣也就實現了空間利用率從1/2到1/3。

MySQL的索引

什麼是索引?
首先索引引入的目的是為了快速查詢以及更新表中的資料。索引是一種儲存在硬碟上的,對資料庫表中一列或多個列進行排序的資料結構。
索引是一個單獨儲存在磁碟上的資料庫結構,它們包含著對資料表裡所有記錄的引用指標,使用索引可以提高資料庫特定資料的查詢速度.索引時在儲存引擎中實現的,因此每種儲存引擎的索引不一定完全相同,並且每種儲存引擎也不一定支援所有索引型別.
為什麼不用二叉搜尋樹
一句話:因為磁碟IO問題。之前說過,索引是儲存在磁碟上的,對資料庫表中一列或多列進行排序的資料結構。資料庫中的資料可能很大,在大量的資料儲存在磁碟上時。計算機無法一次性將資料全部載入進記憶體。而是通過逐一載入每一磁碟頁。而載入磁碟頁對應著索引樹的節點。那麼一次查詢所經歷的索引樹的深度對應著磁碟IO互動的次數。如果採取二叉樹結構,那麼顯而易見,可能導致遍歷的深度太大導致磁碟IO互動的次數太多。而相較於記憶體查詢,磁碟IO才是影響查詢以及更新表中資料的關鍵。那麼由二叉樹這樣的瘦長結構自然容易聯想到如何將它變的矮胖。這樣做雖然沒有降低比較次數,由於深度的降低,可以極大的減少磁碟IO次數。從而可以提升查詢以及更新資料的效能。
B樹相較於二叉搜尋樹的中序遍歷,沒有減少多少,甚至一點都沒有減少,不過降低了磁碟IO的次數。對於提高索引查詢以及更新資料來說。這就足夠了,畢竟磁碟IO才是影響效能的關鍵。
b+樹相比於b樹的查詢優勢

  1. b+樹的中間節點不儲存資料,所以磁碟頁能容納更多節點元素,更“矮胖”;
  2. b+樹查詢必須查詢到葉子節點,b樹只要匹配到即可不用管元素位置,因此b+樹查詢更穩定(並不慢);
  3. 所有葉子節點形成有序連結串列,便於範圍查詢,b+樹只需遍歷葉子節點連結串列即可,b樹卻需要重複地中序遍歷。
    為什麼說B±tree比B 樹更適合實際應用中作業系統的檔案索引和資料庫索引?
    B+tree的磁碟讀寫代價更低:B+tree的內部結點並沒有指向關鍵字具體資訊的指標(紅色部分),因此其內部結點相對B 樹更小。如果把所有同一內部結點的關鍵字存放在同一盤塊中,那麼盤塊所能容納的關鍵字數量也越多。一次性讀入記憶體中的需要查詢的關鍵字也就越多,相對來說IO讀寫次數也就降低了;
    B+tree的查詢效率更加穩定:由於內部結點並不是最終指向檔案內容的結點,而只是葉子結點中關鍵字的索引,所以,任何關鍵字的查詢必須走一條從根結點到葉子結點的路。所有關鍵字查詢的路徑長度相同,導致每一個數據的查詢效率相當;
    資料庫索引採用B+樹而不是B樹的主要原因:B+樹只要遍歷葉子節點就可以實現整棵樹的遍歷,而且在資料庫中基於範圍的查詢是非常頻繁的,而B樹只能中序遍歷所有節點,效率太低。
    檔案索引和資料庫索引為什麼使用B+樹?
      檔案與資料庫都是需要較大的儲存,也就是說,它們都不可能全部儲存在記憶體中,故需要儲存到磁碟上。而所謂索引,則為了資料的快速定位與查詢,那麼索引的結構組織要儘量減少查詢過程中磁碟I/O的存取次數,因此B+樹相比B樹更為合適。資料庫系統巧妙利用了局部性原理與磁碟預讀原理,將一個節點的大小設為等於一個頁,這樣每個節點只需要一次I/O就可以完全載入,而紅黑樹這種結構,高度明顯要深的多,並且由於邏輯上很近的節點(父子)物理上可能很遠,無法利用區域性性。最重要的是,B+樹還有一個最大的好處:方便掃庫。B樹必須用中序遍歷的方法按序掃庫,而B+樹直接從葉子結點挨個掃一遍就完了,B+樹支援range-query非常方便,而B樹不支援,這是資料庫選用B+樹的最主要原因