1. 程式人生 > >B樹、B+樹與B*樹簡介

B樹、B+樹與B*樹簡介

轉自:http://www.tuicool.com/articles/fYz6jy

本文主要介紹各種B樹,不對插入、刪除做過多的深入瞭解。

1. 引子

動態查詢樹主要有:

二叉查詢樹(Binary Search Tree) 平衡二叉查詢樹(Balanced Binary Search Tree) B-tree B+-tree B*-tree  前三者是典型的二叉查詢樹結構,其查詢的 時間複雜度O(log2N)與樹的深度相關,那麼降低樹的深度自然會提高查詢效率 。 但是我們要面對這樣一個實際問題,大規模資料儲存中,實現索引查詢是在這樣一個實際背景下的,即樹節點儲存的元素數量是有限的(如果元素數量非常多的話,查詢就退化成節點內部的線性查找了),這樣 導致二叉查詢樹結構由於樹的深度過大而造成磁碟I/O讀寫過於頻繁,進而導致查詢效率低下,那麼如何減少樹的深度,一個基本的想法就是:採用多叉樹結構。
這樣我們就提出了一個新的查詢樹結構——多路查詢樹。根據平衡二叉樹的啟發,自然就想到平衡多路查詢樹結構,也就是這篇文章所要闡述的第一個主題B-tree(B-tree樹即B樹, B即Balanced ,平衡的意思)。 在開始介紹B-tree之前,先了解下相關的硬體知識,才能很好的瞭解為什麼需要B-tree這種外存資料結構。 

2.外儲存器—磁碟

計算機儲存裝置一般分為兩種:記憶體儲器(main memory)和外儲存器(external memory)。  記憶體存取速度快,但容量小,價格昂貴,而且不能長期儲存資料(在不通電情況下資料會消失)。 外儲存器,一般指磁碟,是一種直接存取的儲存裝置,容量大、資料可以持久儲存。

2.1 磁碟的構造

磁碟是一個扁平的圓盤(與電唱機的唱片類似)。盤面上有許多稱為磁軌的圓圈,資料就記錄在這些磁軌上。磁碟可以是單片的,也可以是由若干碟片組成的盤組,每一碟片上有兩個面。如下圖中所示的6片盤組為例,除去最頂端和最底端的外側面不儲存資料之外,一共有10個面可以用來儲存資訊。                                                                                         
當磁碟驅動器執行讀/寫功能時。碟片裝在一個主軸上,並繞主軸高速旋轉,當磁軌在讀/寫頭(又叫磁頭) 下通過時,就可以進行資料的讀 / 寫了。 一般磁碟分為固定頭盤(磁頭固定)和活動頭盤。 固定頭盤的每一個磁軌上都有獨立的磁頭,它是固定不動的,專門負責這一磁軌上資料的讀/寫。 活動頭盤 (如上圖)的磁頭是可移動的。每一個盤面上只有一個磁頭(磁頭是雙向的,因此正反盤面都能讀寫)。它可以從該面的一個磁軌移動到另一個磁軌。 所有磁頭都裝在同一個動臂上,因此不同盤面上的所有磁頭都是同時移動的(行動整齊劃一)。當碟片繞主軸旋轉的時候,磁頭與旋轉的碟片形成一個圓柱體。各個盤面上半徑相同的磁軌組成了一個圓柱面,我們稱為柱面 。因此,柱面的個數也就是盤面上的磁軌數。 

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

磁碟上資料必須用一個三維地址唯一標示:柱面號、盤面號、塊號(磁軌上的盤塊)。 讀/寫磁碟上某一指定資料需要下面3個步驟: (1) 首先移動臂根據柱面號使磁頭移動到所需要的柱面上,這一過程被稱為定位或查詢 。 (2) 如上圖中所示的6盤組示意圖中,所有磁頭都定位到了10個盤面的10條磁軌上(磁頭都是雙向的)。這時根據盤面號來確定指定盤面上的磁軌。 (3) 盤面確定以後,碟片開始旋轉,將指定塊號的磁軌段移動至磁頭下。 經過上面三個步驟,指定資料的儲存位置就被找到。這時就可以開始讀/寫操作了。 訪問某一具體資訊,由3部分時間組成: ● 查詢時間(seek time) Ts: 完成上述步驟(1)所需要的時間。這部分時間代價最高,最大可達到0.1s左右,一般可以在10ms左右。 ● 等待時間(latency time) Tl: 完成上述步驟(3)所需要的時間。由於碟片繞主軸旋轉速度很快,一般為7200轉/分。因此一般旋轉一圈大約8ms左右。 ● 傳輸時間(transmission time) Tt: 資料通過系統匯流排傳送到記憶體的時間,一般傳輸一個位元組(byte)大概0.02us=2*10^(-8)s 磁碟讀取資料是以盤塊(block)為基本單位的。位於同一盤塊中的所有資料都能被一次性全部讀取出來。而磁碟IO代價主要花費在查詢時間上。因此我們應該儘量將相關資訊存放在同一盤塊,同一磁軌中。或者至少放在同一柱面或相鄰柱面上,以求在讀/寫資訊時儘量減少磁頭來回移動的次數,避免過多的查詢時間。 所以,在大規模資料儲存方面,大量資料儲存在外存磁碟中,而在外存磁碟中讀取/寫入塊(block)中某資料時,首先需要定位到磁碟中的某塊,如何有效地查詢磁碟中的資料, 儘量減少隨機IO ,需要一種合理高效的外存資料結構,就是下面所要重點闡述的B-tree結構,以及相關的變種結構:B+-tree結構和B*-tree結構。

3.B樹  

3.1 定義

B樹又叫平衡多路查詢樹。B樹是為了磁碟或其它儲存裝置而設計的一種多叉(下面你會看到,相對於二叉,B樹每個內結點有多個分支,即多叉)平衡查詢樹。與紅黑樹很相似,但在降低磁碟I/0操作方面要更好一些。 許多資料庫系統都一般使用B樹或者B樹的各種變形結構,如下文即將要介紹的B+樹,B*樹來儲存資訊。                                       

3.2 特點

B樹與紅黑樹最大的不同在於,B樹的結點可以有許多子女,從幾個到幾千個。那為什麼又說B樹與紅黑樹很相似呢?因為與紅黑樹一樣,一棵含n個結點的 B樹的高度也為O(lgn) ,但可能比一棵紅黑樹的高度小許多,應為它的分支因子比較大。所以, B樹可以在O(logn)時間內,實現各種如插入(insert),刪除(delete)等動態集合操作。 在B樹中,一個內結點x若含有n[x]個關鍵字,那麼x將含有n[x]+1個子女,例如下圖,如含有2個關鍵字D H的內結點有3個子女,而含有3個關鍵字Q T X的內結點有4個子女。 一棵m階的B樹 (m叉樹)的特性如下: 1)樹中每個結點最多含有m個孩子(m>=2); 2)除根結點和葉子結點外,其它每個結點至少有[ceil(m / 2)]個孩子(其中ceil(x)是一個取上限的函式); 3)若根結點不是葉子結點,則至少有2個孩子(特殊情況:沒有孩子的根結點,即根結點為葉子結點,整棵樹只有一個根節點); 4)所有葉子結點都出現在同一層,葉子結點不包含任何關鍵字資訊(可以看做是外部接點或查詢失敗的接點,實際上這些結點不存在,指向這些結點的指標都為null) 5)每個非終端結點中包含有n個關鍵字資訊: (n,P0,K1,P1,K2,P2,……,Kn,Pn)。其中:        a)   Ki (i=1…n)為關鍵字,且關鍵字按順序升序排序K(i-1)< Ki。         b)   Pi為指向子樹根的接點,且指標P(i-1)指向子樹種所有結點的關鍵字均小於Ki,但都大於K(i-1)。         c)   關鍵字的個數n必須滿足: [ceil(m / 2)-1]<= n <= m-1。 針對上面第5點,再闡述下:B樹中每一個結點能包含的關鍵字(如之前上面的D H和Q T X)數有一個上界和下界。這個下界可以用一個稱作B樹的最小度數(演算法導論中文版上譯作度數,最小度數即內節點中節點最小孩子數目)t(t>=2)表示。 每個非根的結點必須至少含有t-1個關鍵字。每個非根的內結點至少有t個子女。如果樹是非空的,則根結點至少包含一個關鍵字; 每個結點可包含之多2t-1個關鍵字。所以一個內結點至多可有2t個子女。如果一個結點恰好有2t-1個關鍵字,我們就說這個結點是滿的(而稍後介紹的B*樹作為B樹的一種常用變形, B*樹中要求每個內結點至少為2/3滿,而不是像這裡的B樹所要求的至少半滿); B樹中的每個結點根據實際情況可以包含大量的關鍵字資訊和分支(當然是不能超過磁碟塊的大小,根據磁碟驅動(disk drives)的不同,一般塊的大小在1k~4k左右);這樣樹的深度降低了,這就意味著查詢一個元素只要很少結點從外存磁碟中讀入記憶體,很快訪問到要查詢的資料。

3.3 查詢舉例

假如每個盤塊可以正好存放一個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樹查詢效率的決定因素。 當然,如果我們使用平衡二叉樹的磁碟儲存結構來進行查詢,磁碟4次,最多5次,而且檔案越多,B樹比平衡二叉樹所用的磁碟IO操作次數將越少,效率也越高。

4. B+樹

4.1 定義

B+-tree:是應檔案系統所需而產生的一種B-tree的變形樹。 一棵m階的B+樹和m階的B樹的差異在於:       1.有n棵子樹的結點中含有n個關鍵字; (而B樹是n棵子樹有n-1個關鍵字)       2.所有的葉子結點中包含了全部關鍵字的資訊,及指向含有這些關鍵字記錄的指標,且葉子結點本身依關鍵字的大小自小而大的順序連結。 (而B 樹的葉子節點並沒有包括全部需要查詢的資訊)       3. 所有的非終端結點可以看成是索引部分,結點中僅含有其子樹根結點中最大(或最小)關鍵字。 (而B樹的非終節點也包含需要查詢的有效資訊)  

4.2 特點

B+-tree比B樹更適合實際應用中作業系統的檔案索引和資料庫索引,原因有如下幾點: 1) B+-tree的磁碟讀寫代價更低 B+-tree的內部結點並沒有指向關鍵字具體資訊的指標。因此其內部結點相對B 樹更小。如果把所有同一內部結點的關鍵字存放在同一盤塊中,那麼盤塊所能容納的關鍵字數量也越多。一次性讀入記憶體中的需要查詢的關鍵字也就越多。相對來說IO讀寫次數也就降低了。 2) B+-tree的查詢效率更加穩定 由於非終結點並不是最終指向檔案內容的結點,而只是葉子結點中關鍵字的索引。所以任何關鍵字的查詢必須走一條從根結點到葉子結點的路。所有關鍵字查詢的路徑長度相同,導致每一個數據的查詢效率相當。

4.3 B+樹的分裂

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

5.B*-tree

B*-tree是B+-tree的變體,在B+ 樹非根和非葉子結點再增加指向兄弟的指標;B*樹定義了非葉子結點關鍵字個數至少為(2/3)*M,即塊的最低使用率為2/3(代替B+樹的1/2)。給出了一個簡單例項,如下圖所示: B*樹的分裂:當一個結點滿時,如果它的下一個兄弟結點未滿,那麼將一部分資料移到兄弟結點中,再在原結點插入關鍵字,最後修改父結點中兄弟結點的關鍵字(因為兄弟結點的關鍵字範圍改變了);如果兄弟也滿了,則在原結點與兄弟結點之間增加新結點,並各複製1/3的資料到新結點,最後在父結點增加新結點的指標。 所以,B*樹分配新結點的概率比B+樹要低,空間使用率更高;