1. 程式人生 > >查詢演算法之B樹、B+樹

查詢演算法之B樹、B+樹

B樹是為了磁碟或其他儲存裝置而設計的一種多叉平衡查詢樹.磁碟中有兩個機械運動的部分,分別是碟片旋轉和磁臂移動。碟片旋轉就是我們市面上所提的多少轉每分鐘,而磁碟移動則是在碟片旋轉到指定位置以後,移動磁臂後開始進行資料的讀寫,那麼這就存在一個定位到磁碟中的塊的過程,而定位是磁碟的存取中花費的時間比較大的一塊,畢竟機械運動花費的時間要遠遠大於電子於東的時間。當大規模資料儲存到磁碟中的時候,顯然定位是一個非常花費時間的過程,但是我們可以通過B樹進行優化,提高磁碟讀取時定位的效率。
為什麼B類樹可以進行優化呢?我們可以根據B類樹的特點,構造一個多階B類樹,然後在儘量多的節點上儲存相關的資訊,保證層數儘可能的少,以便後面我們可以更快的找到資訊,磁碟的I/0操作也少一些。樹的高度是和IO次數相關的。

B樹

B樹又叫平衡多路查詢樹一棵m階的B樹的特徵如下:
1、樹的每個節點最多含有m個孩子(m>=2)
2、除根節點和葉子節點外,其他每個節點至少有ceil[m/2]個孩子(ceil取上限)
3、若根節點不是葉子節點,則至少有2個孩子
4、所有的葉子節點都出現在同一層,葉子節點不包含任何關鍵字資訊(可以看做是外部節點或查詢失敗的節點,實際上這些節點不存在,指向這些節點的指標都為null)
5、每個非終端節點中包含有n個關鍵字資訊(P1,K1,P2,K2,P3,……Kn,Pn+1).其中:
a)、ki為關鍵字,且關鍵字按順序升序排序
b)、pi為指向子樹的根節點,且指標p(i)指向子樹中所有節點的關鍵字均小於ki,但都大於K(i-1)
c)、關鍵字的個數n必須滿足:[ceil(m/2)-1]<=n<=m-1
這裡寫圖片描述

查詢

模擬查詢檔案29的過程
(1) 根據根結點指標找到檔案目錄的根磁碟塊1,將其中的資訊匯入記憶體。【磁碟IO操作1次】

(2) 此時記憶體中有兩個檔名17,35和三個儲存其他磁碟頁面地址的資料。根據演算法我們發現17<29<35,因此我們找到指標p2。

(3) 根據p2指標,我們定位到磁碟塊3,並將其中的資訊匯入記憶體。【磁碟IO操作2次】

(4) 此時記憶體中有兩個檔名26,30和三個儲存其他磁碟頁面地址的資料。根據演算法我們發現26<29<30,因此我們找到指標p2。

(5) 根據p2指標,我們定位到磁碟塊8,並將其中的資訊匯入記憶體。【磁碟IO操作3次】

(6) 此時記憶體中有兩個檔名28,29。根據演算法我們查詢到檔案29,並定位了該檔案記憶體的磁碟地址。

插入

生成從空樹開始,逐個插入關鍵字。但是由於B_樹節點關鍵字必須大於等於[ceil(m/2)-1],所以每次插入一個關鍵字不是在樹中新增一個葉子結點, 而是首先在最底層的某個非終端節點中新增一個“關鍵字”,該結點的關鍵字不超過m-1,則插入完成;否則要產生結點的“分裂”,將一半數量的關鍵字元素分裂到新的其相鄰右結點中,中間關鍵字元素上移到父結點中。

1、咱們通過一個例項來逐步講解下。插入以下字元字母到一棵空的B 樹中(非根結點關鍵字數小了(小於2個)就合併,大了(超過4個)就分裂):C N G A H E K Q M F W L T Z D P R X Y S,首先,結點空間足夠,4個字母插入相同的結點中,如下圖:
這裡寫圖片描述
2、當咱們試著插入H時,結點發現空間不夠,以致將其分裂成2個結點,移動中間元素G上移到新的根結點中,在實現過程中,咱們把A和C留在當前結點中,而H和N放置新的其右鄰居結點中。如下圖:
這裡寫圖片描述

3、當咱們插入E,K,Q時,不需要任何分裂操作
這裡寫圖片描述

4、插入M需要一次分裂,注意M恰好是中間關鍵字元素,以致向上移到父節點中

這裡寫圖片描述

5、插入F,W,L,T不需要任何分裂操作
這裡寫圖片描述

6、插入Z時,最右的葉子結點空間滿了,需要進行分裂操作,中間元素T上移到父節點中,注意通過上移中間元素,樹最終還是保持平衡,分裂結果的結點存在2個關鍵字元素。
這裡寫圖片描述

7、插入D時,導致最左邊的葉子結點被分裂,D恰好也是中間元素,上移到父節點中,然後字母P,R,X,Y陸續插入不需要任何分裂操作(別忘了,樹中至多5個孩子)。
這裡寫圖片描述

8、最後,當插入S時,含有N,P,Q,R的結點需要分裂,把中間元素Q上移到父節點中,但是情況來了,父節點中空間已經滿了,所以也要進行分裂,將父節點中的中間元素M上移到新形成的根結點中,注意以前在父節點中的第三個指標在修改後包括D和G節點中。這樣具體插入操作的完成。
這裡寫圖片描述

B+樹

一棵m階的B+樹和m階的b數的差異在於:
1、有n棵子樹的節點中含有n個關鍵字;(B樹是n棵子樹中有n-1個關鍵字)
2、所有葉子節點中包含了全部關鍵字的資訊,及指向含有這些關鍵字記錄的指標,且葉子節點本身依關鍵字的大小自小而大的順序連結(而B樹的葉子節點並沒有包括全部需要查詢的資訊)
3、所有的非終端節點可以看成是索引部分,節點中僅含有其子樹根節點中最大(最小)的關鍵字。而B樹的非終結點也需要包含需要查詢的有效資訊。
這裡寫圖片描述

###為什麼說B+tree比B 樹更適合實際應用中作業系統的檔案索引和資料庫索引?
B+tree的內部結點並沒有指向關鍵字具體資訊的指標。因此其內部結點相對B 樹更小。如果把所有同一內部結點的關鍵字存放在同一盤塊中,那麼盤塊所能容納的關鍵字數量也越多。一次性讀入記憶體中的需要查詢的關鍵字也就越多。相對來說IO讀寫次數也就降低了。

舉個例子,假設磁碟中的一個盤塊容納16bytes,而一個關鍵字2bytes,一個關鍵字具體資訊指標2bytes。一棵9階B-tree(一個結點最多8個關鍵字)的內部結點需要2個盤快。而B+ 樹內部結點只需要1個盤快。當需要把內部結點讀入記憶體中的時候,B 樹就比B+ 樹多一次盤塊查詢時間(在磁碟中就是碟片旋轉的時間)。

2) B+tree的查詢效率更加穩定

由於非終結點並不是最終指向檔案內容的結點,而只是葉子結點中關鍵字的索引。所以任何關鍵字的查詢必須走一條從根結點到葉子結點的路。所有關鍵字查詢的路徑長度相同,導致每一個數據的查詢效率相當。

資料庫索引採用B+樹的主要原因是 B樹在提高了磁碟IO效能的同時並沒有解決元素遍歷的效率低下的問題。正是為了解決這個問題,B+樹應運而生。B+樹只要遍歷葉子節點就可以實現整棵樹的遍歷。而且在資料庫中基於範圍的查詢是非常頻繁的,而B樹不支援這樣的操作(或者說效率太低)。