高性能Mysql 入門到放棄 之 B+-Tree (與B-Tree以及Binary Tree的對比解析)

最近開始看高性能Mysql 入門到放棄。對沒錯就是下面這本,放棄請輸入 sudo rm -rfpython

問題由來:

索引:你們日常說的還有用的索引,若是沒特別標明或者聲明都是B-Tree索引,大多數Mysql引擎都支持這種索引,而Msyql經常使用引擎InnoDB等常爲B+-Tree。sql

提出問題:

!!!注意是B樹 B樹 不是讀做B減樹 B-Tree,怎麼讀內心要有B數的。數據庫

WTF,B-Tree是什麼Tree,是BinaryTree麼?並非那麼什麼是B+-Tree呢?有什麼區別?咱們日常用的二叉搜索樹的時間複雜度不是Log(n)麼,難道不夠優秀麼?數據結構

開始解決問題:

預備知識iphone

磁盤IO性能

· 系統讀取磁盤是將磁盤的基本單位---磁盤塊(Block)讀取出來。位一同一磁盤塊的數據會被一次性讀取出來。磁盤讀取IO是機械動做,時間大概爲內存讀取的十萬多倍。因此磁盤IO讀寫速度成爲索引性能的主要指標。大數據

1.複習一下二叉搜索樹(Binay Search Tree,即BST)

如圖,爲二叉搜索樹,它的時間複雜度爲O(Log(n))。咱們如今要執行搜索,那麼考慮下最好的狀況就是,搜索0009,也就是搜索的關鍵字爲BST的根節點,讀取一次IO;那麼最壞的狀況顯而易見就是樹最深的底層葉子節點(深度爲N那麼爲N次磁盤IO)。

例如 :要索引0010,那麼要進行四次磁盤IO操做,這已經比其餘數據結構少不少次了。優化

能不能優化,怎麼優化操作系統

· 若是優化要優化BST最壞的狀況設計

· 最壞狀況是由樹的深度決定的,也就是說它是N階BST,那麼咱們要壓縮它

· 壓縮 ?什麼意思? 最近世界盃(以英格蘭爲例子),就是把BST(大竹竿克勞奇)變成B-Tree(小土豆魯尼)

左爲魯尼,右邊爲克勞奇~

2 由以前的思考,引出了B-Tree:

B-Tree---平衡多路查找樹

· B-Tree是爲磁盤等外存儲設備設計的一種平衡查找樹。

· B-Tree結構的數據可讓系統高效的找到數據所在的磁盤塊。爲了描述B-Tree,首先定義一條記錄爲一個二元組[key, data],key爲記錄的鍵值,對應表中的主鍵值,data爲一行記錄中除主鍵外的數據。對於不一樣的記錄,key值互不相同。(能夠以python中的字典中的鍵值對理解,鍵惟一且不可改變,就是一個索引的標記,而內容存在data裏面)

一棵m階的B-Tree有以下特性:

每棵樹都有好多屬性概念,這裏加粗的是咱們用的,是對咱們理解B-Tree有關的。

1. 每一個節點最多有m個孩子。

  1. 除了根節點和葉子節點外,其它每一個節點至少有Ceil(m/2)個孩子。
  2. 若根節點不是葉子節點,則至少有2個孩子
  3. 全部葉子節點都在同一層,且不包含其它關鍵字信息
  4. 每一個非終端節點包含n個關鍵字信息(P0,P1,…Pn, k1,…kn)
  5. 關鍵字的個數n知足:ceil(m/2)-1 <= n <= m-1
  6. ki(i=1,…n)爲關鍵字,且關鍵字升序排序。
  7. Pi(i=1,…n)爲指向子樹根節點的指針。P(i-1)指向的子樹的全部節點關鍵字均小於ki,但都大於k(i-1)

直接上圖,這就是一個B-Tree,紫色爲Key,黃色爲data,藍色爲指針,仔細看發現會有磁盤塊,結合以前講的磁盤IO操做,能夠梳理清楚B-Tree的索引流程

每一個節點佔用一個盤塊的磁盤空間,一個節點上有兩個升序排序的關鍵字和三個指向子樹根節點的指針,指針存儲的是子節點所在磁盤塊的地址。兩個關鍵詞劃分紅的三個範圍域對應三個指針指向的子樹的數據的範圍域。以根節點爲例,關鍵字爲17和35,P1指針指向的子樹的數據範圍爲小於17,P2指針指向的子樹的數據範圍爲17~35,P3指針指向的子樹的數據範圍爲大於35。

例子查找60

模擬查找關鍵字60的過程:

根據根節點找到磁盤塊1,讀入內存。【磁盤I/O操做第1次】 比較關鍵字60大於區間(17,35),找到磁盤塊1的指針P3。 根據P3指針找到磁盤塊4,讀入內存。【磁盤I/O操做第2次】 比較關鍵字60小於在區間(65,87),找到磁盤塊3的指針P1。 根據P2指針找到磁盤塊9,讀入內存。【磁盤I/O操做第3次】 在磁盤塊9中的關鍵字列表中找到關鍵字60。 分析上面過程,發現須要3次磁盤I/O操做,和3次內存查找操做。因爲內存中的關鍵字是一個有序表結構,能夠利用二分法查找提升效率。而3次磁盤I/O操做是影響整個B-Tree查找效率的決定因素。B-Tree使每次磁盤I/O取到內存的數據都發揮了做用,從而提升了查詢效率。

!!!:注意比以前BST多了在每個磁盤頁的索引比較,可是由於磁盤頁已經被磁盤IO讀取操做讀入了內存中,因此內存IO操做比磁盤IO操做省時不少根本不是一個數量級的能夠忽略不計,因此磁盤IO操做仍然是最重要的索引性能關鍵。

顯而易見,此次咱們進行了三次磁盤IO比以前的BST少了一次,試想一下在複雜的B-Tree索引中,會減小不少次磁盤IO操做,因此B-Tree更適合索引。

那麼還能夠再優化麼?

iphone6/7/8 升級了就是 iphone6+/7+/8+ ,是的加個+(plus),看來都這麼幹。

B+-Tree:

B+Tree是在B-Tree基礎上的一種優化,使其更適合實現外存儲索引結構,InnoDB存儲引擎就是用B+Tree實現其索引結構。

以前咱們記得B-Tree的索引和關鍵字key-data對存在磁盤裏面,而後被磁盤IO操做讀入內存,那麼在磁盤頁中,若是數據很大呢,若是是大數據呢!!!!

若是數據大的話,磁盤頁沒法裝載,會使得一頁的key的數量減小,仍是會使得B-Tree的深度增長,這樣仍是會增長磁盤IO查詢,計算機科學的老前輩就有一個大膽的想法,那麼我把數據所有放在Tree的葉子節點呢。

在B+Tree中,全部數據記錄節點都是按照鍵值大小順序存放在同一層的葉子節點上,而非葉子節點上只存儲key值信息,這樣能夠大大加大每一個節點存儲的key值數量,下降B+Tree的高度。

B+Tree相對於B-Tree有幾點不一樣:

非葉子節點只存儲鍵值信息。 全部葉子節點之間都有一個鏈指針。 數據記錄都存放在葉子節點中。

B+-Tree圖

一般在B+Tree上有兩個頭指針,一個指向根節點,另外一個指向關鍵字最小的葉子節點,並且全部葉子節點(即數據節點)之間是一種鏈式環結構。所以能夠對B+Tree進行兩種查找運算:一種是對於主鍵的範圍查找和分頁查找,另外一種是從根節點開始,進行隨機查找。

圖中爲彙集索引(clustered index),即範圍查找,從跟節點開始。

!!!B-Tree與B+-Tree的區別!!!

· B+Tree的磁盤讀寫更低,由於非葉子節點能夠存儲更多的索引key,而key索引在同一層更集中,那麼會下降磁盤IO讀寫次數。

· B+Tree的查詢效率更穩定,因爲最終指向文件內容的節點是葉子節點中關鍵字的索引,因此任何關鍵只查找都必須走從根節點到葉子節點的索引路徑,路徑是類似的(深度),因此更穩定(最好最壞狀況都在最底層)。

· 區間訪問性友好Mysql是關係型數據庫,因此常常會按照區間來訪問某個索引,B+樹的葉子節點會按順序創建起鏈狀指針,增強區間訪問性。

最後一個問題:

也是最重要的爲何Mysql會使用B/B+樹來實現索引呢?

· Mysql是基於磁盤的數據庫,索引是以索引文件的形式轉存於磁盤中的

· 索引的過程就是要涉及磁盤IO的消耗,磁盤IO消耗比內存IO消耗要搞好幾個數量級, 即索引涉及的在查找關鍵字時儘可能減小磁盤IO消耗。

附加問題:

操做系統、數據庫、或者說Mysql數據庫引擎爲何使用B+-Tree的多呢?

· 由於B-Tree仍然未解決關鍵字的遍歷,若是要遍歷B-Tree上的因此關鍵字時間複雜度很是高,可是B+-Tree就不同了,能夠直接遍歷葉子節點,支持範圍索引,由於OS DB DB ENGINE 都常用範圍索引,故 B+-Tree更勝一籌。

打完收工~

相關文章
相關標籤/搜索