從MongoDB及mysql 談B/B+樹

一 B樹的由來

B樹指的是一類樹,包括B-樹,B+樹,B*樹等,是一種自平衡的搜索樹,它相似普通的平衡二叉樹,不一樣的一點是B樹容許每一個節點有更多的子節點。B樹是專門爲外部存儲器設計的,如磁盤,它對於讀取和寫入大塊數據有良好的性能,因此通常用在文件系統及數據庫中。

1. 爲何不用二叉平衡樹

傳統用來搜索的平衡二叉樹有不少,AVL樹,紅黑樹等。這些樹在通常狀況下的查詢性能很是好,但當數據量很是大的時候就無能爲力了。數據量很是大時,內存不夠用,大部分數據只能存放在磁盤上,只有須要的數據才加載到內存。通常而言內存的訪問時間爲50ns,而磁盤在10ms左右。速度相差了近5個數量級,程序大部分的時間阻塞在磁盤IO上。因此核心就是要減小磁盤IO次數。而像AVL樹,紅黑樹這類平衡二叉樹從設計上沒法"迎合"磁盤。mysql

上圖是一顆簡單的平衡二叉樹,咱們來看下爲何數據庫和文件系統用b樹而不用平衡二叉樹:redis

(1) 它的平衡是經過旋轉來實現的,而旋轉是對整個樹的操做,若部分加載到內存中則沒法完成旋轉操做。sql

(2) 平衡二叉樹的高度比較大(log n), 這樣邏輯上很近的節點實際可能很是遠,沒法很好地利用磁盤預讀(空間的局部性原理)數據庫

 

2. 從"迎合"磁盤的角度來看看B樹的設計

索引的效率實際上依賴於磁盤IO的次數,加快索引的辦法就是有效地減小磁盤IO次數。相比於平衡二叉樹每次將範圍分割爲兩個區間,B樹每次將範圍分割成多個區間,區間越多,定位數據越快越精確。多叉下降了B樹的高度(底數很大的log n,二叉是底數爲2). 那麼節點爲區間範圍,每一個節點就比較大了。所以新建節點時,直接申請頁大小的空間(磁盤時按照block分的,通常爲512 byte。磁盤IO一次讀取若干個block,咱們稱爲一頁,具體大小和操做系統有關,通常爲4k,8k或16k),計算機內存分配是按頁對齊的,這樣一個節點只須要一次IO。那麼多叉樹的總IO次數也就縮減爲log n次。json

 

 

二 B-樹

1. B-樹的結構

上面是一顆B-樹,B-樹的插入和刪除就不具體介紹了,不少資料都描述了這一過程。在普通平衡二叉樹中,插入刪除後若不知足平衡條件則進行 旋轉 操做,而在B-樹中,插入刪除後不知足條件則進行分裂及合併操做。nosql

2. B-樹的查找

咱們來看看B-樹的查找,假設每一個節點有 n 個 key值,被分割爲 n+1 個區間,注意,每一個 key 值緊跟着 data 域,這說明B-樹的 key 和 data 是聚合在一塊兒的。通常而言,根節點都在內存中,B-樹以每一個節點爲一次磁盤 IO,好比上圖中,若搜索 key 爲 25 節點的 data,首先在根節點進行二分查找(由於 keys 有序,二分最快),判斷 key 25 小於 key 50,因此定位到最左側的節點,此時進行一次磁盤 IO,將該節點從磁盤讀入內存,接着繼續進行上述過程,直到找到該 key 爲止。性能

 

 

三 B+樹

1. B+樹的結構

B+樹是B-樹的變種,它與B-樹的不一樣之處在於:mysql索引

  • 在B+樹中,key 的副本存儲在內部節點,真正的 key 和 data 存儲在葉子節點上 。
  • n 個 key 值的節點指針域爲 n 而不是 n+1

 

由於內節點並不存儲 data,因此通常B+樹的葉節點和內節點大小不一樣,而B-樹的每一個節點大小通常是相同的,爲一頁。優化

爲了增長 區間訪問性,通常會對B+樹作一些優化。 
以下圖帶順序訪問的B+樹。網站

 

 

三 B-樹和B+樹的區別

1. 時間複雜度

B+樹節點不存儲數據,全部data存儲在葉節點致使查詢時間負責度固定爲log n。而B-樹查詢時間複雜度不固定,與key在樹中的位置有關,最好爲O(1)

下面爲圖示:

B-樹

從上圖能夠看出,key 爲 50 的節點就在第一層,B-樹只須要一次磁盤 IO 便可完成查找。因此說B-樹的查詢最好時間複雜度是 O(1)

 

B+樹

因爲B+樹全部的 data 域都在根節點,因此查詢 key 爲 50的節點必須從根節點索引到葉節點,時間複雜度固定爲 O(log n)

 

2. 區間查找

B+樹葉節點兩兩相連可大大增長區間訪問性,可以使用在範圍查詢等,而B-樹每一個節點key 和data在一塊兒,則沒法區間查找

B+樹能夠很好的利用局部性原理,若咱們訪問節點 key爲 50,則 key 爲 5五、60、62 的節點未來也可能被訪問,咱們能夠利用磁盤預讀原理提早將這些數據讀入內存,減小了磁盤 IO 的次數。 
固然B+樹也可以很好的完成範圍查詢。好比查詢 key 值在 50-70 之間的節點。

 

3. B+樹更適合外部存儲。因爲內節點無 data 域,每一個節點能索引的範圍更大更精確

這個很好理解,因爲B-樹節點內部每一個 key 都帶着 data 域,而B+樹節點只存儲 key 的副本,真實的 key 和 data 域都在葉子節點存儲。前面說過磁盤是分 block 的,一次磁盤 IO 會讀取若干個 block,具體和操做系統有關,那麼因爲磁盤 IO 數據大小是固定的,在一次 IO 中,單個元素越小,量就越大。這就意味着B+樹單次磁盤 IO 的信息量大於B-樹,從這點來看B+樹相對B-樹磁盤 IO 次數少。

從上圖能夠看出相同大小的區域,B-樹僅有 2 個 key,而B+樹有 3 個 key。

 

 

三 爲何MongoDB索引選擇B-樹,而mysql索引選擇B+樹

1 mysql 和 MongoDB

mysql是傳統的關係型數據庫,而MongoDB是文檔形的數據庫,是一種nosql,它使用xml或json格式來保存數據,歸屬於聚合型數據庫。(鍵值數據庫也屬於聚合型數據庫,好比redis)

2 例子🌰

咱們要創建一個電子商務網站,相似淘寶這種將商品銷售給用戶,那麼必須存儲用戶信息、商品目錄、訂單、收貨地址、帳單地址、付款方式等。

(1)傳統的關係型數據庫模型

(2)聚合型數據庫存儲模型

用相似json的格式表示以下:

//Customer
{
        "id":1,
        "name":Tom,
        "billingAddress":[{"city":"China"}]
}

//Orders
{
        "id":99,
        "orderItem":[
                "productId"27,
                "price":100,
                "productName":book
         ],
         "shippingAddress":[{"city":"china"}],
         "orderPayment":[
            ...
        ]   
}

相對於 Mysql 關係型數據庫,MongoDB 這類 nosql 適用於數據模型簡單,性能要求高的場合

 

爲何 MongoDB 使用B-樹

MongoDB 是一種 nosql,也存儲在磁盤上,被設計用在 數據模型簡單,性能要求高的場合。上面說過,儘可能減小磁盤IO是提升性能的有效手段,而B-樹剛好key和data域聚合在一塊兒,複雜度最好爲O(1)。

 

 4 爲何 Mysql 使用B+樹

(1) Mysql 是一種關係型數據庫,區間訪問是常見的一種狀況,而 B-樹並不支持區間訪問,而B+樹因爲數據所有存儲在葉子節點,而且經過指針串在一塊兒,這樣就很容易的進行區間遍歷甚至所有遍歷

(2) 其次B+樹的查詢效率更加穩定,數據所有存儲在葉子階段,查詢時間複雜度固定爲O(log n)。

(3) B+樹更適合外部存儲,因爲內節點無data域,每一個節點能索引的範圍更大更精確

 

摘自: http://blog.csdn.net/wwh578867817/article/details/50493940

相關文章
相關標籤/搜索