B樹、B+樹、B*樹 總結
文章詳細摘自:https://blog.csdn.net/v_july_v/article/details/6530142
1.1磁碟的構造
磁碟是一個扁平的圓盤(與電唱機的唱片類似)。盤面上有許多稱為磁軌的圓圈,資料就記錄在這些磁軌上。磁碟可以是單片的,也可以是由若干碟片組成的盤組,每一碟片上有兩個面。如下圖11.3中所示的6片盤組為例,除去最頂端和最底端的外側面不儲存資料之外,一共有10個面可以用來儲存資訊。
當磁碟驅動器執行讀/寫功能時。碟片裝在一個主軸上,並繞主軸高速旋轉,當磁軌在讀/寫頭(又叫磁頭) 下通過時,就可以進行資料的讀 /寫了。
一般磁碟分為固定頭盤
1.2磁碟的讀/寫原理和效率
磁碟上資料必須用一個三維地址唯一標示:柱面號、盤面號、塊號(磁軌上的盤塊)。
讀
(1) 首先移動臂根據柱面號使磁頭移動到所需要的柱面上,這一過程被稱為定位或查詢。
(2) 如上圖11.3中所示的6盤組示意圖中,所有磁頭都定位到了10個盤面的10條磁軌上(磁頭都是雙向的)。這時根據盤面號來確定指定盤面上的磁軌。
(3) 盤面確定以後,碟片開始旋轉,將指定塊號的磁軌段移動至磁頭下。
經過上面三個步驟,指定資料的儲存位置就被找到。這時就可以開始讀/寫操作了。
訪問某一具體資訊,由3部分時間組成:
● 查詢時間(seek time) Ts: 完成上述步驟(1)所需要的時間。這部分時間代價最高,最大可達到0.1s左右。
● 等待時間
● 傳輸時間(transmission time) Tt: 資料通過系統匯流排傳送到記憶體的時間,一般傳輸一個位元組(byte)大概0.02us=2*10^(-8)s
磁碟讀取資料是以盤塊(block)為基本單位的。位於同一盤塊中的所有資料都能被一次性全部讀取出來。而磁碟IO代價主要花費在查詢時間Ts上。因此我們應該儘量將相關資訊存放在同一盤塊,同一磁軌中。或者至少放在同一柱面或相鄰柱面上,以求在讀/寫資訊時儘量減少磁頭來回移動的次數,避免過多的查詢時間Ts。
所以,在大規模資料儲存方面,大量資料儲存在外存磁碟中,而在外存磁碟中讀取/寫入塊(block)中某資料時,首先需要定位到磁碟中的某塊,如何有效地查詢磁碟中的資料,需要一種合理高效的外存資料結構,就是下面所要重點闡述的B-tree結構,以及相關的變種結構:B+-tree結構和B*-tree結構。
2.1、B 樹又叫平衡多路查詢樹。
為了簡單,這裡用少量資料構造一棵3叉樹的形式,實際應用中的B樹結點中關鍵字很多的。上面的圖中比如根結點,其中17表示一個磁碟檔案的檔名;小紅方塊表示這個17檔案內容在硬碟中的儲存位置;p1表示指向17左子樹的指標。
其結構可以簡單定義為:
typedef struct {
/*檔案數*/
int file_num;
/*檔名(key)*/
char * file_name[max_file_num];
/*指向子節點的指標*/
BTNode * BTptr[max_file_num+1];
/*檔案在硬碟中的儲存位置*/
FILE_HARD_ADDR offset[max_file_num];
}BTNode;
假如每個盤塊可以正好存放一個B樹的結點(正好存放2個檔名)。那麼一個BTNODE結點就代表一個盤塊,而子樹指標就是存放另外一個盤塊的地址。
下面,咱們來模擬下查詢檔案29的過程:
-
根據根結點指標找到檔案目錄的根磁碟塊1,將其中的資訊匯入記憶體。【磁碟IO操作 1次】
-
此時記憶體中有兩個檔名17、35和三個儲存其他磁碟頁面地址的資料。根據演算法我們發現:17<29<35,因此我們找到指標p2。
-
根據p2指標,我們定位到磁碟塊3,並將其中的資訊匯入記憶體。【磁碟IO操作 2次】
-
此時記憶體中有兩個檔名26,30和三個儲存其他磁碟頁面地址的資料。根據演算法我們發現:26<29<30,因此我們找到指標p2。
-
根據p2指標,我們定位到磁碟塊8,並將其中的資訊匯入記憶體。【磁碟IO操作 3次】
-
此時記憶體中有兩個檔名28,29。根據演算法我們查詢到檔名29,並定位了該檔案記憶體的磁碟地址。
分析上面的過程,發現需要3次磁碟IO操作和3次記憶體查詢操作。關於記憶體中的檔名查詢,由於是一個有序表結構,可以利用折半查詢提高效率。至於IO操作是影響整個B樹查詢效率的決定因素。
2.2.B+-tree 是應檔案系統所需而產生的一種B-tree的變形樹。
一棵m階的B+樹和m階的B樹的異同點在於:
1.所有的葉子結點中包含了全部關鍵字的資訊,及指向含有這些關鍵字記錄的指標,且葉子結點本身依關鍵字的大小自小而大的順序連結。 (而B 樹的葉子節點並沒有包括全部需要查詢的資訊)
2.所有的非終端結點可以看成是索引部分,結點中僅含有其子樹根結點中最大(或最小)關鍵字。 (而B 樹的非終節點也包含需要查詢的有效資訊)
為什麼說B+-tree比B 樹更適合實際應用中作業系統的檔案索引和資料庫索引?
1) B+-tree的磁碟讀寫代價更低
B+-tree的內部結點並沒有指向關鍵字具體資訊的指標。因此其內部結點相對B 樹更小。如果把所有同一內部結點的關鍵字存放在同一盤塊中,那麼盤塊所能容納的關鍵字數量也越多。一次性讀入記憶體中的需要查詢的關鍵字也就越多。相對來說IO讀寫次數也就降低了。
舉個例子,假設磁碟中的一個盤塊容納16bytes,而一個關鍵字2bytes,一個關鍵字具體資訊指標2bytes。一棵9階B-tree(一個結點最多8個關鍵字)的內部結點需要2個盤快。而B+ 樹內部結點只需要1個盤快。當需要把內部結點讀入記憶體中的時候,B 樹就比B+ 樹多一次盤塊查詢時間(在磁碟中就是碟片旋轉的時間)。
2) B+-tree的查詢效率更加穩定
由於非終結點並不是最終指向檔案內容的結點,而只是葉子結點中關鍵字的索引。所以任何關鍵字的查詢必須走一條從根結點到葉子結點的路。所有關鍵字查詢的路徑長度相同,導致每一個數據的查詢效率相當。
讀者點評
3)資料庫索引採用B+樹的主要原因是 B樹在提高了磁碟IO效能的同時並沒有解決元素遍歷的效率低下的問題。正是為了解決這個問題,B+樹應運而生。B+樹只要遍歷葉子節點就可以實現整棵樹的遍歷。而且在資料庫中基於範圍的查詢是非常頻繁的,而B樹不支援這樣的操作(或者說效率太低)。
2.3.B*-tree
B*-tree是B+-tree的變體,在B+樹的基礎上(所有的葉子結點中包含了全部關鍵字的資訊,及指向含有這些關鍵字記錄的指標),B*樹中非根和非葉子結點再增加指向兄弟的指標;B*樹定義了非葉子結點關鍵字個數至少為(2/3)*M,即塊的最低使用率為2/3(代替B+樹的1/2)。給出了一個簡單例項,如下圖所示:
B+樹的分裂:當一個結點滿時,分配一個新的結點,並將原結點中1/2的資料複製到新結點,最後在父結點中增加新結點的指標;B+樹的分裂隻影響原結點和父結點,而不會影響兄弟結點,所以它不需要指向兄弟的指標。
B*樹的分裂:當一個結點滿時,如果它的下一個兄弟結點未滿,那麼將一部分資料移到兄弟結點中,再在原結點插入關鍵字,最後修改父結點中兄弟結點的關鍵字(因為兄弟結點的關鍵字範圍改變了);如果兄弟也滿了,則在原結點與兄弟結點之間增加新結點,並各複製1/3的資料到新結點,最後在父結點增加新結點的指標。
所以,B*樹分配新結點的概率比B+樹要低,空間使用率更高;