1. 程式人生 > >B樹、B+樹、B*樹 總結

B樹、B+樹、B*樹 總結


文章詳細摘自:https://blog.csdn.net/v_july_v/article/details/6530142

1.1磁碟的構造

磁碟是一個扁平的圓盤(與電唱機的唱片類似)。盤面上有許多稱為磁軌的圓圈,資料就記錄在這些磁軌上。磁碟可以是單片的,也可以是由若干碟片組成的盤組,每一碟片上有兩個面。如下圖11.3中所示的6片盤組為例,除去最頂端和最底端的外側面不儲存資料之外,一共有10個面可以用來儲存資訊。

                           

當磁碟驅動器執行讀/寫功能時。碟片裝在一個主軸上,並繞主軸高速旋轉,當磁軌在讀/寫頭(又叫磁頭下通過時,就可以進行資料的讀 /寫了。

一般磁碟分為固定頭盤

(磁頭固定)和活動頭盤。固定頭盤的每一個磁軌上都有獨立的磁頭,它是固定不動的,專門負責這一磁軌上資料的讀/寫。活動頭盤 (如上圖)的磁頭是可移動的。每一個盤面上只有一個磁頭(磁頭是雙向的,因此正反盤面都能讀寫)。它可以從該面的一個磁軌移動到另一個磁軌。所有磁頭都裝在同一個動臂上,因此不同盤面上的所有磁頭都是同時移動的(行動整齊劃一)。當碟片繞主軸旋轉的時候,磁頭與旋轉的碟片形成一個圓柱體。各個盤面上半徑相同的磁軌組成了一個圓柱面,我們稱為柱面。因此,柱面的個數也就是盤面上的磁軌數。

1.2磁碟的讀/寫原理和效率

磁碟上資料必須用一個三維地址唯一標示:柱面號、盤面號、塊號(磁軌上的盤塊)。

/寫磁碟上某一指定資料需要下面3個步驟:

(1)  首先移動臂根據柱面號使磁頭移動到所需要的柱面上,這一過程被稱為定位或查詢

(2)  如上圖11.3中所示的6盤組示意圖中,所有磁頭都定位到了10個盤面的10條磁軌上(磁頭都是雙向的)。這時根據盤面號來確定指定盤面上的磁軌。

(3) 盤面確定以後,碟片開始旋轉,將指定塊號的磁軌段移動至磁頭下。

經過上面三個步驟,指定資料的儲存位置就被找到。這時就可以開始讀/寫操作了。

訪問某一具體資訊,由3部分時間組成:

● 查詢時間(seek time) Ts: 完成上述步驟(1)所需要的時間。這部分時間代價最高,最大可達到0.1s左右。

● 等待時間

(latency time) Tl: 完成上述步驟(3)所需要的時間。由於碟片繞主軸旋轉速度很快,一般為7200/(電腦硬碟的效能指標之一,家用的普通硬碟的轉速一般有5400rpm(筆記本)7200rpm幾種)因此一般旋轉一圈大約0.0083s

● 傳輸時間(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. 根據根結點指標找到檔案目錄的根磁碟塊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,並定位了該檔案記憶體的磁碟地址。

分析上面的過程,發現需要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*-treeB+-tree的變體,在B+樹的基礎上(所有的葉子結點中包含了全部關鍵字的資訊,及指向含有這些關鍵字記錄的指標),B*樹中非根和非葉子結點再增加指向兄弟的指標;B*樹定義了非葉子結點關鍵字個數至少為(2/3)*M,即塊的最低使用率為2/3(代替B+樹的1/2)。給出了一個簡單例項,如下圖所示:

B+樹的分裂:當一個結點滿時,分配一個新的結點,並將原結點中1/2的資料複製到新結點,最後在父結點中增加新結點的指標;B+樹的分裂隻影響原結點和父結點,而不會影響兄弟結點,所以它不需要指向兄弟的指標。

B*樹的分裂:當一個結點滿時,如果它的下一個兄弟結點未滿,那麼將一部分資料移到兄弟結點中,再在原結點插入關鍵字,最後修改父結點中兄弟結點的關鍵字(因為兄弟結點的關鍵字範圍改變了);如果兄弟也滿了,則在原結點與兄弟結點之間增加新結點,並各複製1/3的資料到新結點,最後在父結點增加新結點的指標。

所以,B*樹分配新結點的概率比B+樹要低,空間使用率更高;