前面講解了平衡查詢樹中的2-3樹以及其實現紅黑樹。2-3樹種,一個節點最多有2個key,而紅黑樹則使用染色的方式來標識這兩個key。
維基百科對B樹的定義為“在電腦科學中,B樹(B-tree)是一種樹狀資料結構,它能夠儲存資料、對其進行排序並允許以O(log n)的時間複雜度執行進行查詢、順序讀取、插入和刪除的資料結構。B樹,概括來說是一個節點可以擁有多於2個子節點的二叉查詢樹。與自平衡二叉查詢樹不同,B-樹為系統最優化大塊資料的讀和寫操作。B-tree演算法減少定位記錄時所經歷的中間過程,從而加快存取速度。普遍運用在資料庫和檔案系統。”
定義
B 樹可以看作是對2-3查詢樹的一種擴充套件,即他允許每個節點有M-1個子節點。
- 根節點至少有兩個子節點
- 每個節點有M-1個key,並且以升序排列
- 位於M-1和M key的子節點的值位於M-1 和M key對應的Value之間
- 其它節點至少有M/2個子節點
下圖是一個M=4 階的B樹:
可以看到B樹是2-3樹的一種擴充套件,他允許一個節點有多於2個的元素。
B樹的插入及平衡化操作和2-3樹很相似,這裡就不介紹了。下面是往B樹中依次插入
6 10 4 14 5 11 15 3 2 12 1 7 8 8 6 3 6 21 5 15 15 6 32 23 45 65 7 8 6 5 4
的演示動畫:
B+樹是對B樹的一種變形樹,它與B樹的差異在於:
- 有k個子結點的結點必然有k個關鍵碼;
- 非葉結點僅具有索引作用,跟記錄有關的資訊均存放在葉結點中。
- 樹的所有葉結點構成一個有序連結串列,可以按照關鍵碼排序的次序遍歷全部記錄。
如下圖,是一個B+樹:
下圖是B+樹的插入動畫:
B和B+樹的區別在於,B+樹的非葉子結點只包含導航資訊,不包含實際的值,所有的葉子結點和相連的節點使用連結串列相連,便於區間查詢和遍歷。
B+ 樹的優點在於:
- 由於B+樹在內部節點上不好含資料資訊,因此在記憶體頁中能夠存放更多的key。 資料存放的更加緊密,具有更好的空間區域性性。因此訪問葉子幾點上關聯的資料也具有更好的快取命中率。
- B+樹的葉子結點都是相鏈的,因此對整棵樹的便利只需要一次線性遍歷葉子結點即可。而且由於資料順序排列並且相連,所以便於區間查詢和搜尋。而B樹則需要進行每一層的遞迴遍歷。相鄰的元素可能在記憶體中不相鄰,所以快取命中性沒有B+樹好。
但是B樹也有優點,其優點在於,由於B樹的每一個節點都包含key和value,因此經常訪問的元素可能離根節點更近,因此訪問也更迅速。下面是B 樹和B+樹的區別圖:
分析
對B樹和B+樹的分析和對前面講解的2-3樹的分析類似,
對於一顆節點為N度為M的子樹,查詢和插入需要logM-1N ~ logM/2N次比較。這個很好證明,對於度為M的B樹,每一個節點的子節點個數為M/2 到 M-1之間,所以樹的高度在logM-1N至logM/2N之間。
這種效率是很高的,對於N=62*1000000000個節點,如果度為1024,則logM/2N <=4,即在620億個元素中,如果這棵樹的度為1024,則只需要小於4次即可定位到該節點,然後再採用二分查詢即可找到要找的值。
應用
B樹和B+廣泛應用於檔案儲存系統以及資料庫系統中,在講解應用之前,我們看一下常見的儲存結構:
我們計算機的主存基本都是隨機訪問儲存器(Random-Access Memory,RAM),他分為兩類:靜態隨機訪問儲存器(SRAM)和動態隨機訪問儲存器(DRAM)。SRAM比DRAM快,但是也貴的多,一般作為CPU的快取記憶體,DRAM通常作為記憶體。這類儲存器他們的結構和儲存原理比較複雜,基本是使用電訊號來儲存資訊的,不存在機器操作,所以訪問速度非常快,具體的訪問原理可以檢視CSAPP,另外,他們是易失的,即如果斷電,儲存DRAM和SRAM儲存的資訊就會丟失。
我們使用的更多的是使用磁碟,磁碟能夠儲存大量的資料,從GB一直到TB級,但是 他的讀取速度比較慢,因為涉及到機器操作,讀取速度為毫秒級,從DRAM讀速度比從磁碟度快10萬倍,從SRAM讀速度比從磁碟讀快100萬倍。下面來看下磁碟的結構:
如上圖,磁碟由碟片構成,每個碟片有兩面,又稱為盤面(Surface),這些盤面覆蓋有磁性材料。碟片中央有一個可以旋轉的主軸(spindle),他使得碟片以固定的旋轉速率旋轉,通常是5400轉每分鐘(Revolution Per Minute,RPM)或者是7200RPM。磁碟包含一個多多個這樣的碟片並封裝在一個密封的容器內。上圖左,展示了一個典型的磁碟表面結構。每個表面是由一組成為磁軌(track)的同心圓組成的,每個磁軌被劃分為了一組扇區(sector).每個扇區包含相等數量的資料位,通常是(512)子節。扇區之間由一些間隔(gap)隔開,不儲存資料。
以上是磁碟的物理結構,現在來看下磁碟的讀寫操作:
如上圖,磁碟用讀/寫頭來讀寫儲存在磁性表面的位,而讀寫頭連線到一個傳動臂的一端。通過沿著半徑軸前後移動傳動臂,驅動器可以將讀寫頭定位到任何磁軌上,這稱之為尋道操作。一旦定位到磁軌後,碟片轉動,磁軌上的每個位經過磁頭時,讀寫磁頭就可以感知到位的值,也可以修改值。對磁碟的訪問時間分為 尋道時間,旋轉時間,以及傳送時間。
由於儲存介質的特性,磁碟本身存取就比主存慢很多,再加上機械運動耗費,因此為了提高效率,要儘量減少磁碟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樹,用來進行內容儲存。
另外B/B+樹也經常用做資料庫的索引,這方面推薦您直接看張洋的《MySQL索引背後的資料結構及演算法原理》這篇文章,這篇文章對MySQL中的如何使用B+樹進行索引有比較詳細的介紹,推薦閱讀。
總結
在前面兩篇文章介紹了平衡查詢樹中的2-3樹,紅黑樹之後,本文介紹了檔案系統和資料庫系統中常用的B/B+ 樹,他通過對每個節點儲存個數的擴充套件,使得對連續的資料能夠進行較快的定位和訪問,能夠有效減少查詢時間,提高儲存的空間區域性性從而減少IO操作。他廣泛用於檔案系統及資料庫中,如:
- Windows:HPFS檔案系統
- Mac:HFS,HFS+檔案系統
- Linux:ResiserFS,XFS,Ext3FS,JFS檔案系統
- 資料庫:ORACLE,MYSQL,SQLSERVER等中
希望本文對您瞭解B/B+ 樹有所幫助。