1. 程式人生 > >B樹(B-樹)詳解

B樹(B-樹)詳解

B-樹,即為B樹。因為B樹的原英文名稱為B-tree,而國內很多人喜歡把B-tree譯作B-樹,B-tree就是指的B樹

B-樹容易讓人誤解,建議大家用B樹稱呼, 本文以下直稱B樹

這篇介紹概念, 優點應用等, B樹的描述和增刪改查請到隔壁我寫的另一篇(篇幅較長,和這篇分開了):

https://blog.csdn.net/q5706503/article/details/84729052

簡介:

B樹(英語:B-tree)是一種自平衡的,能夠保持資料有序。這種資料結構能夠讓查詢資料、順序訪問、插入資料及刪除的動作,都在對數時間內完成。B樹,概括來說是一個一般化的二叉查詢樹(binary search tree),可以擁有多於2個子節點。與自平衡二叉查詢樹不同,B樹為系統大塊資料的讀寫操作做了優化。B樹減少定位記錄時所經歷的中間過程,從而加快存取速度。B樹這種資料結構可以用來描述外部儲存。這種資料結構常被應用在

資料庫檔案系統的實現上。

資料庫中的B樹使用

B/B+樹也經常用做資料庫的索引

已排序檔案的查詢時間

通常,排序和查詢演算法會被通過大O符號,刻畫為比較級別的數值。對一個有N筆記錄的已排序表進行二叉查詢,打個比方說,可以在O(log2N)比較級完成。如果表有1,000,000筆記錄,那麼定位其中一筆記錄,將在20 個比較級內完成。 log21,000,000 = 19.931...

大資料庫一直以來被儲存在磁碟。從磁碟上讀取一筆記錄,與之後的比較鍵值操作相比,在花費的執行時間上前者處於支配地位。從磁碟讀取記錄的時間涉及到一個 尋道時間 和 旋轉延遲。尋道時間可能是從0到20或者更多毫秒,旋轉延遲平均下來約是旋轉週期的一半。對於一個7200 轉每分鐘的磁碟,旋轉週期大約是8.33毫秒。像希捷ST3500320NS這樣的磁碟,磁軌至磁軌的尋道時間為 0.8毫秒,平均讀取尋道時間為8.5毫秒。為了簡化,假設從磁碟讀取花費10毫秒。

樂觀來說,如此,在一百萬中定位一筆記錄將會話花費20次磁碟讀取乘上10毫秒每次讀取時間,總共是0.2秒。

時間花費沒有那麼糟糕的原因是,獨立的記錄被成組地記錄在磁碟塊上。一個磁碟塊可能為16 千位元組。如果每筆記錄大小為160 位元組,那麼一個塊可以儲存100 筆記錄。上面假設的磁碟讀取時間確切地說是讀取一個完整塊的時間。一旦磁頭到達位置,一個或者更多的磁碟塊可以以較小的延遲來完成讀取。對於100筆記錄每塊,最後差不多6個比較級是不需要任何磁碟讀取的————都在上次讀取操作中完成了。

為進一步加速查詢,開始的13或14個比較級(每個需要一次磁碟訪問)必須要提速。 [1] 

提升查詢的索引

較大程度上的提升是通過索引來做到的。在上面的例子中,初始磁碟讀取從2個因素限制了查詢範圍。這基本上可以通過建立一個輔助索引來改善,這個索引包含每塊磁碟塊上的首筆記錄(有時稱為稀疏索引)。這個輔助索引可能只有原始資料庫的1%大小,但是它可以更快速地被檢索。在輔助索引中查詢入口可以告訴我們在主資料庫中要讀去哪一塊;查詢輔助索引之後,我們只需要讀取主資料庫中的特定的某一個磁碟分塊————通過一次磁碟讀取開銷。索引可以提供10,000入口,所以,這樣最多需要14個比較級。就像主資料庫,輔助索引中最後6個左右的比較級可能在相同的磁碟分塊上。索引可以在大約8次磁碟讀取中完成查詢,目標記錄會在9次磁碟讀取後獲得。

建立輔助索引的竅門是可以重複地給輔助索引建立輔助索引。那樣可以實現一個只擁有100 入口,能填滿一整個磁碟塊的輔助-輔助索引。

要找到想要的記錄,我們只需要讀取3次磁碟分塊,而不是14次。讀取和查詢輔助-輔助索引中第一個(而且是唯一的)塊,標記了相應的輔助索引中的分塊。讀取和查詢輔助索引的分塊,標記了主資料庫中相應的分塊。我們只需要30毫秒,而不是150毫秒就能獲取記錄。

輔助的索引,使得查詢問題從約為log2N 磁碟讀取開銷的二分查詢,變成logbN 磁碟讀取開銷的查詢,其中b為分塊因素(每分塊的入口數目:b = 100 入口每分塊;logb1,000,000 = 3 次讀取)。

在實際中,如果主資料庫被頻繁查詢,輔助-輔助索引和大部分的輔助索引可能會儲存在磁碟快取中,所以它們不會產生磁碟讀取。

插入和刪除帶來的麻煩

如果資料庫不會改變,那麼編制索引就很簡單,而且索引永遠不需要改變。如果他們會改變,那麼管理資料庫及其索引就變得非常麻煩。

從資料庫中刪除記錄不會引起太大問題。索引可以保持不變,記錄只需要標記為已刪除。資料庫仍然保持有序狀態。如果會有很多刪除,之後查詢和儲存就不再那麼高效了。

在一個有序檔案中進行插入將是個災難,因為需要給插入的記錄製造空間。在檔案中第一筆記錄後插入記錄需要把所有記錄向後偏移一個位置。如此的操作在實際中實在太過昂貴。

一種做法是預留一些空間給插入操作。磁碟塊有一些空閒空間允許後來的插入,而不是高密度地填充。這些記錄可以被標記為像是已刪除的記錄。

現在,只要塊中存在空間,插入和刪除都可以很快速。如果一個插入操作在一個塊上找不到合適的空間,就在臨近的塊中尋找,且要調整輔助索引。期望是臨近存在足夠的空間,以免重新調整大量的塊。作為可選方案,可以使用一些非排序的塊。

B樹運用的理念

B樹使用了以上所有的想法。特別是:

  • 保持鍵值有序,以順序遍歷

  • 使用層次化的索引來最小化磁碟讀取

  • 使用不完全填充的塊來加速插入和刪除

  • 通過優雅的遍歷演算法來保持索引平衡

另外,B樹通過保證內部節點至少半滿來最小化空間浪費。一棵B樹可以處理任意數目的插入和刪除。

檔案系統中的B樹

除了在資料庫中使用之外,B樹也用在檔案系統中,以允許快速隨機訪問特定檔案中的任意塊。基本問題是轉動檔案塊地址到磁碟塊(或者可能是圓柱頭扇區)地址。在講解應用之前,我們看一下常見的儲存結構:

File System

我們計算機的主存基本都是隨機訪問儲存器(Random-Access Memory,RAM),他分為兩類:靜態隨機訪問儲存器(SRAM)和動態隨機訪問儲存器(DRAM)。SRAM比DRAM快,但是也貴的多,一般作為CPU的快取記憶體,DRAM通常作為記憶體。這類儲存器他們的結構和儲存原理比較複雜,基本是使用電訊號來儲存資訊的,不存在機器操作,所以訪問速度非常快,具體的訪問原理可以檢視CSAPP,另外,他們是易失的,即如果斷電,儲存DRAM和SRAM儲存的資訊就會丟失。

我們使用的更多的是使用磁碟,磁碟能夠儲存大量的資料,從GB一直到TB級,但是 他的讀取速度比較慢,因為涉及到機器操作,讀取速度為毫秒級,從DRAM讀速度比從磁碟度快10萬倍,從SRAM讀速度比從磁碟讀快100萬倍。下面來看下磁碟的結構:

Disk geometry

如上圖,磁碟由碟片構成,每個碟片有兩面,又稱為盤面(Surface),這些盤面覆蓋有磁性材料。碟片中央有一個可以旋轉的主軸(spindle),他使得碟片以固定的旋轉速率旋轉,通常是5400轉每分鐘(Revolution Per Minute,RPM)或者是7200RPM。磁碟包含一個多多個這樣的碟片並封裝在一個密封的容器內。上圖左,展示了一個典型的磁碟表面結構。每個表面是由一組成為磁軌(track)的同心圓組成的,每個磁軌被劃分為了一組扇區(sector).每個扇區包含相等數量的資料位,通常是(512)子節。扇區之間由一些間隔(gap)隔開,不儲存資料。

以上是磁碟的物理結構,現在來看下磁碟的讀寫操作:

Disk dynamic

如上圖,磁碟用讀/寫頭來讀寫儲存在磁性表面的位,而讀寫頭連線到一個傳動臂的一端。通過沿著半徑軸前後移動傳動臂,驅動器可以將讀寫頭定位到任何磁軌上,這稱之為尋道操作。一旦定位到磁軌後,碟片轉動,磁軌上的每個位經過磁頭時,讀寫磁頭就可以感知到位的值,也可以修改值。對磁碟的訪問時間分為 尋道時間旋轉時間,以及傳送時間

由於儲存介質的特性,磁碟本身存取就比主存慢很多,再加上機械運動耗費,因此為了提高效率,要儘量減少磁碟I/O,減少讀寫操作。為了達到這個目的,磁碟往往不是嚴格按需讀取,而是每次都會預讀,即使只需要一個位元組,磁碟也會從這個位置開始,順序向後讀取一定長度的資料放入記憶體。這樣做的理論依據是電腦科學中著名的區域性性原理:

當一個數據被用到時,其附近的資料也通常會馬上被使用。

程式執行期間所需要的資料通常比較集中。

由於磁碟順序讀取的效率很高(不需要尋道時間,只需很少的旋轉時間),因此對於具有區域性性的程式來說,預讀可以提高I/O效率。

預讀的長度一般為頁(page)的整倍數。頁是計算機管理儲存器的邏輯塊,硬體及作業系統往往將主存和磁碟儲存區分割為連續的大小相等的塊,每個儲存塊稱為一頁(在許多作業系統中,頁得大小通常為4k),主存和磁碟以頁為單位交換資料。當程式要讀取的資料不在主存中時,會觸發一個缺頁異常,此時系統會向磁碟發出讀盤訊號,磁碟會找到資料的起始位置並向後連續讀取一頁或幾頁載入記憶體中,然後異常返回,程式繼續執行。

檔案系統及資料庫系統的設計者利用了磁碟預讀原理,將一個節點的大小設為等於一個頁,這樣每個節點只需要一次I/O就可以完全載入。為了達到這個目的,在實際實現B-Tree還需要使用如下技巧:

每次新建一個節點的同時,直接申請一個頁的空間( 512或者1024),這樣就保證一個節點物理上也儲存在一個頁裡,加之計算機儲存分配都是按頁對齊的,就實現了一個node只需一次I/O。如,將B樹的度M設定為1024,這樣在前面的例子中,600億個元素中只需要小於4次查詢即可定位到某一儲存位置。

同時在B+樹中,內節點只儲存導航用到的key,並不儲存具體值,這樣內節點個數較少,能夠全部讀取到主存中,外接點儲存key及值,並且順序排列,具有良好的空間區域性性。所以B及B+樹比較適合與檔案系統的資料結構。下面是一顆B樹,用來進行內容儲存。

build a large B tree

他廣泛用於檔案系統及資料庫中,如:

  • Windows:HPFS檔案系統
  • Mac:HFS,HFS+檔案系統
  • Linux:ResiserFS,XFS,Ext3FS,JFS檔案系統

B樹與訪問併發

通過將每個級別的樹塊與“下一個”指標連結在一起,可以避免所有讀鎖(因此併發訪問得到極大改進)。這導致樹結構,其中插入和搜尋操作都從根到葉子。只有在修改樹塊時才需要寫鎖。這最大化了多個使用者的訪問併發性,這是資料庫和/或其他基於B樹的ISAM儲存方法的重要考慮因素。與此改進相關的成本是在正常操作期間無法從btree中刪除空頁。

B樹的弊端

  • 除非完全重建資料庫,否則無法改變鍵值的最大長度。這使得許多資料庫系統將人名截斷到70字元之內。

(其他關聯陣列的實現,例如三元搜尋樹或者開雜湊雜湊表,可以動態適應任意長度的鍵值)。

 

參考以下文章和維基百科(有一部分為谷歌直接翻譯過來的, 可能難以理解, 表示歉意!):

http://www.cnblogs.com/yangecnu/p/Introduce-B-Tree-and-B-Plus-Tree.html