1. 程式人生 > >二叉樹、平衡二叉樹、B-Tree、B+Tree 說明

二叉樹、平衡二叉樹、B-Tree、B+Tree 說明

背景

      一般說MySQL的索引,都清楚其索引主要以B+樹為主,此外還有Hash、RTree、FullText。本文簡要說明一下MySQL的B+Tree索引,以及和其相關的二叉樹、平衡二叉樹、B-Tree,相關的知識網上很多,為了方便自己更快、清楚的瞭解,文字聚合一些內容以及個人的一些理解。

說明

二叉查詢樹(BST)

  • 概念
    二叉查詢樹是基於二分查詢法來提高資料查詢速度的二叉樹的資料結構;關於二分查詢法的時間複雜度可以看 時間複雜度 O(log n) 意味著什麼?。
  • 特點
    二叉查詢樹是採用二分查詢法把資料按規則組裝成一個樹形結構的資料,減少無關資料的檢索,提升了資料檢索的速度;二叉樹的資料結構有以下規則:

    (1)非葉子節點只能允許最多兩個子節點存在。

    (2)每一個非葉子節點資料分佈規則為左邊的子節點小當前節點的值,右邊的子節點大於當前節點的值;
    即二叉查詢樹的特點就是任何節點的左子節點的鍵值都小於當前節點的鍵值,右子節點的鍵值都大於當前節點的鍵值。 頂端的節點稱為根節點,沒有子節點的節點我們稱之為葉節點。以下圖中的圓為二叉查詢樹的節點,節點中儲存了鍵(key)和資料(data)

    查詢結點值的方法就是二分查詢法:查詢次數就是樹的高度。二叉查詢樹可以任意地構造 如果向一方傾斜的二叉樹是不平衡的,查詢效率就低了,二叉查詢樹變成了一個連結串列。如下圖:


    在上面的2張圖中,查詢鍵值為17的資料,第一張圖裡需要3次IO,第2張圖裡需要7次IO。原因是二叉查詢樹變得不平衡了,也就是高度太高了,從而導致查詢效率的不穩定。為了解決這個問題,需要保證二叉查詢樹一直保持平衡,就需要用到平衡二叉樹了。 

平衡二叉樹(AVL)

在滿足二叉查詢樹特性的基礎上,如不是空樹,任何一個結點的左子樹與右子樹都是平衡二叉樹,並且高度之差的絕對值不超過 1。 類似於:

關於平衡二叉樹的可以看 什麼是平衡二叉樹(AVL)該文章說明,平衡二叉樹相比於二叉查詢樹來說,查詢效率更穩定,總體的查詢速度也更快。需要注意的是平衡二叉樹是每個節點只儲存一個鍵值和資料的。

B樹(B-Tree)

  • 概念:
    B樹和平衡二叉樹不同,B樹屬於多叉樹又名平衡多路查詢樹(查詢路徑不只兩個),資料庫索引裡大量使用者B-Tree和B+Tree的資料結構。
  • 特點:

    (1)排序方式:所有節點關鍵字是按遞增次序排列,並遵循左小右大原則;

    (2)子節點數:非葉節點的子節點數>1,且<=M ,且M>=2,空樹除外(注:M階代表一個樹節點最多有多少個查詢路徑,M=M路,當M=2則是2叉樹,M=3則是3叉);

    (3)關鍵字數:枝節點的關鍵字數量大於等於ceil(M/2)-1個且小於等於M-1個(注:ceil()是個朝正無窮方向取整的函式 如ceil(1.1)結果為2);

    (4)所有葉子節點均在同一層、葉子節點除了包含了關鍵字還包含了資料;

     最後我們用一個圖和一個實際的例子來理解B樹(這裡為了理解方便我就直接用實際字母的大小來排列C>B>A)


    圖中可以看到BTree的單個節點可以儲存多個鍵值和資料的平衡樹。和平衡二叉樹相比:
    比如要儲存海量的資料,因為(平衡)二叉樹的每個節點只儲存一個鍵值和資料的,二叉樹的節點將會非常多,高度也會及其高,當查詢資料時也會進行很多次磁碟IO,查詢的效率將會極低,大致的二叉樹結構如下:

    為了解決平衡二叉樹的這個弊端,需要一種單個節點可以儲存多個鍵值和資料的平衡樹(BTree):

    從上圖可以看出,B樹相對於平衡二叉樹,每個節點儲存了更多的鍵值(key)和資料(data),並且每個節點擁有更多的子節點,子節點的個數一般稱為階,上述圖中的B樹為3階B樹,高度也會很低。 基於這個特性,B樹查詢資料讀取磁碟的次數將會很少,資料的查詢效率也會比平衡二叉樹高很多。

    假如我們要查詢id=28的使用者資訊,那麼我們在上圖B樹中查詢的流程如下:
    
    1. 先找到根節點也就是頁1,判斷28在鍵值17和35之間,我們那麼我們根據頁1中的指標p2找到頁3。
    2. 將28和頁3中的鍵值相比較,28在26和30之間,我們根據頁3中的指標p2找到頁8。
    3. 將28和頁8中的鍵值相比較,發現有匹配的鍵值28,鍵值28對應的使用者資訊為(28,bv)。

    區別:B樹相對於平衡二叉樹的不同是:每個節點包含的關鍵字增多了,特別是在B樹應用到資料庫中的時候,資料庫充分利用了磁碟塊的原理(磁碟資料儲存是採用塊的形式儲存的,每個塊的大小為4K,每次IO進行資料讀取時,同一個磁碟塊的資料可以一次性讀取出來)把節點大小限制和充分使用在磁碟快大小範圍;把樹的節點關鍵字增多後樹的層級比原來的二叉樹少了,減少資料查詢的次數和複雜度。
    相同數量的key在B-Tree中生成的節點要遠遠少於二叉樹中的節點,相差的節點數量就等同於磁碟IO的次數。這樣到達一定數量後,效能的差異就顯現出來了。

B+樹(B+Tree)

  • 概念
    B+樹是B樹的一個進化,相對於B樹來說B+樹更充分的利用了節點的空間,讓查詢速度更加穩定,其速度完全接近於二分法查詢。結構如下:

    為什麼說B+樹查詢的效率要比B樹更高、更穩定;我們先看看兩者的區別:

    (1)B+樹的非葉子節點不儲存資料,只進行資料索引(關鍵字記錄的指標),這樣使得B+樹每個非葉子節點所能儲存的關鍵字大大增加;

    (2)B+樹葉子節點儲存了父節點的所有關鍵字記錄的指標,所有資料地址必須要到葉子節點才能獲取到。所以每次資料查詢的次數都一樣;

    (3)B+樹葉子節點的關鍵字從小到大有序排列,左邊結尾資料都會儲存右邊節點開始資料的指標;

    (4)B+樹非葉子節點的子節點數=關鍵字數;

  • 特點

    1、B+樹的層級更少:相較於B樹,B+每個非葉子節點儲存的關鍵字數更多,樹的層級更少所以查詢資料更快;

    2、B+樹查詢速度更穩定:B+所有關鍵字資料地址都存在葉子節點上,所以每次查詢的次數都相同所以查詢速度要比B樹更穩定;

    3、B+樹天然具備排序功能:B+樹所有的葉子節點資料構成了一個有序連結串列,在查詢大小區間的資料時候更方便,資料緊密性很高,快取的命中率也會比B樹高。

    4、B+樹全節點遍歷更快:B+樹遍歷整棵樹只需要遍歷所有的葉子節點即可,而不需要像B樹一樣需要對每一層進行遍歷,這有利於資料庫做全表掃描。

    B樹相對於B+樹的優點是:如果經常訪問的資料離根節點很近,而B樹的非葉子節點本身存有關鍵字其資料的地址,所以這種資料檢索的時候會要比B+樹快。

    根據上圖我們來看下B+樹和B樹有什麼不同:
    1. B+Tree 非葉子節點上是不儲存資料的,僅儲存鍵值,資料儲存在同一層的葉節點,而B-Tree節點中不僅儲存鍵值,也會儲存資料。之所以這麼做是因為在資料庫中頁的大小是固定的,innodb中頁的預設大小是16KB。如果不儲存資料,那麼就會儲存更多的鍵值,相應的樹的階數(節點的子節點樹)就會更大,樹就會更矮更胖,如此一來我們查詢資料進行磁碟的IO次數有會再次減少,資料查詢的效率也會更快。另外,B+Tree的階數是等於鍵值的數量的,如果B+Tree一個節點可以儲存1000個鍵值,那麼3層B+樹可以儲存1000×1000×1000=10億個資料。一般根節點是常駐記憶體的,所以一般我們查詢10億資料,只需要2次磁碟IO。
    2. 因為B+Tree索引的所有資料均儲存在葉子節點,而且資料是按照順序排列的。那麼B+樹使得範圍查詢,排序查詢,分組查詢以及去重查詢變得異常簡單。而B-Tree 因為資料分散在各個節點,要實現這一點是很不容易的。

    B+Tree 中各個頁之間是通過雙向連結串列連線的,葉子節點中的資料是通過單向連結串列連線的。

    其實上面的B-Tree也可以對各個節點加上鍊表。其實這些不是它們之前的區別,是因為在mysql的innodb儲存引擎中,索引就是這樣儲存的。也就是說上圖中的B+Tree索引就是innodb中B+Tree索引真正的實現方式,準確的說應該是聚集索引。

    通過上圖可以看到,在innodb中,資料頁之間通過雙向連結串列連線以及葉子節點中資料之間通過單向連結串列連線的方式可以找到表中所有的資料。

    注意:MyISAM中的B+樹索引實現與innodb中的略有不同。在MyISAM中,B+樹索引的葉子節點並不儲存資料,而是儲存資料的檔案地址。 

總結

   B+Tree 結構是從二叉查詢樹,平衡二叉樹和B-Tree這三種資料結構演化來的,他們之前的區別上面已經介紹過,現在大致的總結下,如下:

  1,二叉查詢樹是基於二分查詢法來提高資料查詢速度的二叉樹的資料結構,減少無關資料的檢索,提升了資料檢索的速度。非葉子節點只能允許最多兩個子節點存在,每一個非葉子節點資料分佈規則為左邊的子節點小當前節點的值,右邊的子節點大於當前節點的值,每個節點只儲存一個鍵值和資料的。

  2,平衡二叉樹滿足二叉查詢樹特性的基礎上,如不是空樹,任何一個結點的左子樹與右子樹都是平衡二叉樹,並且高度之差的絕對值不超過 1。

  3,B-TreeB和平衡二叉樹不同,B-Tree屬於多叉樹又名平衡多路查詢樹, B-Tree相對於平衡二叉樹,每個節點儲存了更多的鍵值(key)和資料(data),並且每個節點擁有更多的子節點。

  4,B+Tree和B-Tree不同,B+Tree在非葉子節點上,不儲存資料,只儲存鍵指標,能儲存更多的鍵值,相應的樹的階數(節點的子節點樹)就會更大,樹就會更矮更胖,如此一來我們查詢資料進行磁碟的IO次數有會再次減少,資料查詢的效率也會更快。並且B+樹索引的所有資料均儲存在葉子節點,而且資料是按照順序排列的。那麼B+Tree使得範圍查詢,排序查詢,分組查詢以及去重查詢變得異常簡單。

 

參考文章:

https://database.51cto.com/art/201911/605365.htm

https://zhuanlan.zhihu.com/p/27700617

https://www.cnblogs.com/dongguacai/p/7239599.html

https://www.cnblogs.com/leefreeman/p/8315844.html

&n