談到索引,你們並不陌生。索引自己是一種數據結構,存在的目的主要是爲了縮短數據檢索的時間,最大程度減小磁盤 IO。數據庫
任何有數據的場景幾乎都有索引,好比手機通信錄、文件系統(ext4xfsntfs)、數據庫系統(MySQLOracle)。數據庫系統和文件系統通常都採用 B+ 樹來存儲索引信息,B+ 樹兼顧寫和讀的性能,最極端時檢索複雜度爲 O(logN),其中 N 指的是節點數量,logN 表示對磁盤 IO 掃描的總次數。數據結構
MySQL 支持的索引結構有四種:B+ 樹,R 樹,HASH,FULLTEXT。性能
本篇簡單介紹下 B+ 樹,下一篇講 MySQL 經常使用的兩種引擎 MyISAM 和 InnoDB 的 B+ 樹索引實現,其他的後面會講到。優化
再講什麼是 B+ 樹以前,先來了看下什麼是二叉樹。spa
樹自己是一種數據存儲結構,由於相似現實生活中的樹而命名。3d
一個看似沒有修剪過的樹,其實這是一棵二叉樹,每一個節點最多有兩個子節點。指針
樹相關的基礎概念:blog
拿圖 1 這棵樹舉例說明:排序
平衡因子:某節點的左子樹與右子樹深度的差值,通常結果爲絕對值。索引
圖 1 是一顆很是普通的樹,很是容易退化爲一張鏈表。若是把圖 1 換成以下圖, 根節點就變爲 4,6 退化爲 4 的兒子節點,這棵樹就退化爲一張鏈表。
鏈表的查找很是慢,只能按照節點順序查找,每一個節點都遍歷一遍,時間複雜度爲 O(n),沒法隨機查找。
那對圖 1 進行下改造,把數據從新節點從新鏈接下,圖 2 以下:
圖 2 能夠看到如下特性:
1. 全部左子樹的節點都小於其對應的父節點(4,5,6)<(7);(4)<(5);(8)< (9);
2. 全部右子樹上的節點都大於其對應的父節點(8,9,10)>(7);(6)>(5);(10)>(9);
3. 每一個節點的平衡因子差值絕對值 <=1;
4. 每一個節點都符合以上三個特徵。
知足這樣條件的樹叫平衡二叉樹(AVL)樹。
問:那再次查找節點 5,須要遍歷多少次呢?
因爲數據是按照順序組織的,那查找起來很是快,從上往下找:7-5,只須要在左子樹上查找,也就是遍歷 2 次就找到了 5。假設要找到葉子節點 10,只須要在右子樹上查找,那也最多須要 3 次,7-9-10。也就說 AVL 樹在查找方面性能很好,最壞的狀況是找到一個節點須要消耗的次數也就是樹的層數, 複雜度爲 O(logN)
若是節點很是多呢?假設如今有 31 個節點,用 AVL 樹表示如圖 3:
圖 3 是一棵高度爲 4 的 AVL 樹,有 5 層共 31 個節點,橙色是 ROOT 節點,藍色是葉子節點。對 AVL 樹的查找來看起來已經很完美了,能不能再優化下?好比,可否把這個節點裏存放的 KEY 增長?可否減小樹的總層數?那減小縱深只能從橫向來想辦法,這時候能夠考慮用多叉樹。
B 樹是一種多叉的 AVL 樹。B-Tree 減小了 AVL 數的高度,增長了每一個節點的 KEY 數量。
B 樹的特性:(m 爲階數:結點的孩子個數最大值)
1. 樹中每一個節點最多含有 m 個孩子節點 (m>=2);
2. 除根節點和葉子結點外,其餘節點的孩子數量 >=ceil(m / 2);
3. 若根節點不是葉子結點,最少有兩個孩子
4. 每一個非葉子結點中包含有 n 個關鍵字信息:(n,P0,K1,P1,K2,P2,......,Kn,Pn) 其中:
按照這個要求,把圖 3 簡單變爲一棵 B 樹,見圖 4:
圖 4 是一棵 4 階 B 樹,總共有 11 個節點,節點數比圖 3 少了 20 個;層數爲 3,比圖 3 少了兩層。實際應用中,每一個最小單元不是 KEY,而通常是按照塊(BLOCK)來算。好比磁盤文件系統 EXT4 每塊 4KB;數據庫好比 PostgreSQL 是 8KB,MySQL InnoDB 是 16KB, MySQL NDB 是 32KB 等。
因此再次理清圖 4 的 B 樹,變爲圖 5:
圖 5 每一個節點的基本單元是一個磁盤塊(BLOCK,默認 4KB),根節點含有一個鍵值,其餘節點含有 3 個鍵值,每一個磁盤塊包含對應的鍵值與數據。
好比如今要讀取 KEY 爲 31 的記錄:先找到根節點磁盤塊(1),讀入內存。(第一次 IO);關鍵字 31 大於區間(16,),根據指針 P2 找到磁盤塊 3,讀入內存(第二次 IO);31 大於區間(20,24,28),根據指針 P4 讀取磁盤塊 11(第三次 IO),在磁盤塊 11 中找到 KEY 爲 31 的記錄,返回結果。這期間有三次磁盤 IO 的讀取。能夠明確看到,B 樹相對於 AVL 樹,減小了樹的節點數與樹的深度,減小了磁盤 IO。
看到這裏其實有一個問題,三次 IO,前兩次 IO 其實從磁盤讀取了沒必要要的數據,由於只用比較 KEY,因此非葉子節點對應的 DATA 徹底沒有必要,若是 DATA 很大,那徹底是浪費內存資源。考慮下可否把非葉子節點的 DATA 拿掉?
B+ 樹是對 B 樹的一個小升級。大部分數據庫的索引都是基於 B+ 樹存儲的。MySQL 的 MyISAM 和 InnoDB 引擎的索引都是基於 B+ 樹存儲。
B+ 樹最大的幾個特色:
1. 非葉子節點只保留 KEY,放棄 DATA;
2. KEY 和 DATA一塊兒,在葉子節點,而且保存爲一個有序鏈表(正序,反序,或者雙向);
3. B+ 樹的查找與 B 樹不一樣,當某個結點的 KEY 與所查的 KEY 相等時,並不中止查找,而是沿着這個 KEY 左邊的指針向下,一直查到該關鍵字所在的葉子結點爲止。
那對圖 5 的 B 樹作一個調整,變爲如下 B+ 樹,見圖 6:
圖 6 是一棵 6 階 B+ 樹。不一樣於圖 5,非葉子節點再也不包含除了主鍵外的數據,數據所有放在葉子節點,而且全部葉子節點存放在一個單向鏈表裏,固然也能夠雙向鏈表。能夠看到,B+ 樹同時具備平衡多叉樹和鏈表的優勢,便可兼顧 B 樹對範圍查找的高效,又可兼顧鏈表隨機寫入的高效, 這也是大部分數據庫都用 B+ 樹來存儲索引的緣由。
本篇是爲了下一篇介紹 MySQL 的兩種經常使用引擎:MyISAM 和 InnoDB 索引結構作了一個鋪墊,下期見。
關於 MySQL 的技術內容,大家還有什麼想知道的嗎?趕忙留言告訴小編吧!