MySQL 筆記 - 索引類型

寫在前面

這,只是一篇讀《高性能 MySQL》的讀書筆記,以爲水的同窗麻煩右上角關閉哈,感謝~算法

索引類型包括 B-Tree、哈希索引、R-Tree、全文索引等,這裏主要總結 B-Tree 和哈希索引。數據庫

B-Tree

說索引以前就不得不先說一說 B-Tree。服務器

B-Tree 是一種平衡搜索樹,結構相似於普通的二叉樹,區別在於每一個結點容許有更多的子結點。函數

B-Tree 結構

B-Tree,這裏的圖直接引用了參考中第一篇文章的圖~若有侵權,煩請私信我~性能

image-20180806224627469

B+Tree 結構

B+Tree 是 B-Tree 的變種,也是一種平衡二叉樹,B+Tree 如圖所示,這裏的圖直接引用了參考中第一篇文章的圖~若有侵權,煩請私信我~優化

image-20180806224734127

B-Tree 的定義

這一部分引用算導的定義方法~加密

一顆 B-Tree T 具備如下性質,.net

  • 每一個結點 x 有如下屬性:
    • x.n,表示存儲在結點 x 中的關鍵字個數
    • x.n 個關鍵字自己非降序存放
    • x.leaf 表示 x 是否爲葉子節點
  • 內部結點包含 x.n+1 個指針指向它的孩子
  • 每一個葉子結點具備相同的深度,即樹的高度h=log_t \frac{n+1}{2}
  • 每一個結點中的關鍵字個數有上界和下界,這裏使用 B-Tree 的最小度即 t 來表示,t \ge 2
    • 除了根結點和葉子結點外,每一個結點的孩子個數爲 t \leq n \leq 2t
    • 除了根結點和葉子結點外,每一個結點的關鍵字個數爲t-1 \leq n \leq 2t-1
  • B+Tree 中非葉子結點的孩子個數和關鍵字個數相同

B-Tree vs B+Tree

看完圖,來總結一下~設計

B-Tree,指針

  • 每一個結點上都有對應數據的存儲
  • 每一個關鍵字出現且僅在一個結點上出現
  • 搜索可能在非葉子結點結束

B+Tree,

  • 非葉子結點不存儲數據
  • 葉子結點增長了一個雙向鏈表,因此從圖中也能夠看出,葉子結點上包含了全部關鍵字

B+Tree 的優點,

  • 因爲非葉子結點不存儲實際的數據,因此內存中存儲更多的關鍵字,也就是說單次磁盤 IO 信息量會大於 B-Tree
  • 葉子結點增長了順序鏈表,更適合區間查詢

Why B-Tree

事實上,紅黑樹也能夠用做索引,爲何 MySQL 中使用的是 B/B+ Tree 來實現索引嘞。由於 MySQL 是基於磁盤的數據庫,索引的查找過程勢必會涉及到磁盤 IO,因此索引性能的兩個關鍵點就是,

  • 磁盤 IO 的次數
  • CPU 計算速度

因此,在設計索引的時候就要儘可能減小磁盤 IO 的次數,而 MySQL 將記錄按照頁的方式進行管理,B-Tree 每次新建結點的時候,直接申請一個頁的空間,這樣就保證了一個結點物理上也存儲在一個頁裏,而且計算機存儲分配都是按頁對齊,因此就實現了一個結點只須要一次 IO。在樹中一次檢索最多須要 h - 1 次 IO,由於根結點常駐內存,B-Tree 由於能夠有不少結點個數,因此 h 很小,而結點的個數與頁的大小相關,一樣的數據,紅黑樹的 h 明顯要深不少,因此一般都是用 B/B+Tree 做爲索引結構。

B-Tree 的漸進時間複雜度爲,這裏記 N 爲關鍵字個數,d 爲內結點出度的 1/2,O(h)=O(log_dN)O(h)=O(log_dN)

紅黑樹的漸進時間複雜度爲,O(h)

因此,爲何 MySQL 使用 B/B+Tree 來實現索引也就不言而喻啦。

B-Tree 的操做

這裏針對 B-Tree 的一些操做就不給出圖示了,由於算導中已經給出僞代碼以及很是詳細的圖示啦~只作一個簡單總結,

  • 插入關鍵字:先找到關鍵字應該插入的葉子結點,若是結點是滿的,即關鍵字個數爲 2t-1,此時應該根據這個葉子結點中的關鍵字的中間的關鍵字進行分裂,中間的關鍵字會被上移到該節點的父節點中,若是父節點也是滿的,那麼重複上述操做,直到根結點爲止,若是到根結點,那麼就說明高度增長了一層
  • 刪除關鍵字:刪除關鍵字要比插入關鍵字複雜一些,由於刪除的不僅是葉子結點,還能夠是內結點,這時候咱們就須要考慮如何安置內結點的孩子們,而且還要保證刪除後的 B-Tree 符合要求,因此刪除關鍵字分幾種狀況,
    • 若是 k 在結點 x 中,且 x 爲葉子結點,那麼直接從 x 中刪除 k 便可
    • 若是 x 中前於 k 的結點 u1 中的關鍵字個數爲 t,那麼找到 u1 中最大的關鍵字 key,刪除 u1 中的 key,並在 x 中用 key 代替 k
    • 若是 x 中前於 k 的節點 u1 中的關鍵字個數小於 t,那麼找到 x 中後於 k 的結點 u2,若是 u2 中的關鍵字個數爲 t,那麼找到 u2 中最小的關鍵字 key,刪除 u2 中的 key,並在 x 中用 key 代替 k
    • 若是先後兩個節點的關鍵字個數都是 t-1,那麼合併 u1 和 u2,並在 x 中刪除 k,將 x 中的指針指向新的結點
    • 若是 k 不在當前的內結點中,那麼找到 k 所在的內結點後,執行上述操做便可

B-Tree 索引

以 B-Tree 爲結構的索引是最多見的索引類型,好比 InnoDB 和 MyISAM 都是以 B-Tree 爲索引結構的索引,事實上是以 B+ Tree 爲索引結構,B-Tree 和 B+Tree 區別在於,B+ Tree 在葉子節點上增長了順序訪問指針,方便葉子節點的範圍遍歷。這裏主要介紹一下 InnoDB 和 MyISAM 兩種不一樣的索引。

InnoDB

InnoDB 支持聚簇索引,聚簇索引和非聚簇索引嚴格來講不是一種索引,而是一種數據存儲方式,這個名字跟它自己的存儲方式有關係,「聚簇「表示數據行和相鄰的鍵值存儲在一塊兒,簡單的說,就是葉子節點中存儲的實際是真實的數據。InnoDB 經過主鍵彙集數據,因此一個表只能有一個聚簇索引,且必須有主鍵,若是沒有定義主鍵,且不存在非空索引能夠代替,InnoDB 會隱式定義一個主鍵做爲聚簇索引。

聚簇索引的二級索引存儲的不是指向行的物理位置的指針,而是行的主鍵值,因此若是經過二級索引查找行,須要找到二級索引的葉子結點得到對應的主鍵值,而後再去查找對應的行。對於 InnoDB,自適應哈希索引能夠減小這樣的重複工做。

MyISAM

MyISAM 支持非聚簇索引,和 InnoDB 的區別在於,葉子結點上存的是指向對應行的物理地址,也就是說索引和數據實際是分開存儲的。

MyISAM 採用了前綴壓縮技術,從而使得更多索引能夠放入到內存中,默認只壓縮字符串,可是經過參數設置也能夠對整數作壓縮。壓縮方法爲,完整保存索引塊中的第一個IE之,而後將其餘值和第一個值進行比較獲得相同前綴的字節數和剩餘的不一樣後綴部分,把這部分存儲起來便可。例如,索引塊中的第一個值是「perform」,第二個值是「performance」,那麼第二個值的前綴壓縮後存儲的是相似「7,ance」這樣的形式。MyISAM 對行指針也採用相似的前綴壓縮方式。

壓縮可使索引使用更少的空間,在某種程度上性能有所提高,可是代價是某些操做可能更慢。由於每一個值的壓縮前綴都依賴前面的值,因此 MyISAM 查找時沒法在索引塊使用二分查找,只能從頭開始掃描,正序掃描速度還不錯,逆序掃描的話查找某一行的操做平均都須要掃描半個索引塊。對於 CPU 密集型應用,掃描須要隨機查找,因此壓縮索引會使得索引查找慢不少,而對於 I/O 密集型應用,對於某些查詢的優化更明顯。

InnoDB 使用的是行鎖,因此支持事務,而 MyISAM 使用的是表鎖,不支持事務。

適用範圍

B-Tree 索引適用於區間查詢,由於 B-Tree 存儲後的葉子節點自己就是有序的,而且 B+ Tree 結構還增長了葉子節點的連續順序指針,對於區間查詢來講就更加方便了。

哈希索引

哈希索引是基於哈希表實現的,只有精確匹配索引全部列的查詢纔有效。方法是,對全部的索引列計算一個 hash code,hash code 做爲索引,在哈希表中保存指向每一個數據行的指針。

優勢

  • 索引自己只存儲 hash code,因此結構很緊湊,而且查找速度很快

限制

  • 索引中的 hash code 是順序存儲的,可是 hash code 對應的數據並非順序的,因此沒法用於排序
  • 不支持部分索引列匹配查找,由於哈希索引是使用索引列的所有內容來計算 hash code
  • 只支持等值比較,不支持範圍查詢
  • 若是哈希衝突嚴重時,必須遍歷鏈表中全部行指針
  • 哈希衝突嚴重的話,索引維護操做的代價也很高

InnoDB 的自適應哈希索引

首先,請注意,自適應哈希索引對於用戶來講是無感知的,這是一個徹底自動、內部的行爲,用戶沒法控制或者配置,可是能夠關閉。

當 InnoDB 注意到某個索引值被使用的很是頻繁時,它會在內存中基於 B-Tree 索引之上再建立一個哈希索引,這樣 B-Tree 也能夠具備哈希索引的一些優勢,好比快速的哈希查找。

固然若是存儲引擎不支持哈希索引,用戶也能夠自定義哈希索引,這樣性能會比較高,缺陷是須要本身維護哈希值,若是採用這種方法,不要使用 SHA1()MD5() 做爲哈希函數,由於這兩個是強加密函數,設計目標是最大限度消除衝突,生成的 hash code 是一個很是長的字符串,浪費大量的空間,哈希索引中對於索引的衝突要求沒有那麼高。

索引的優勢

  • 使用索引能夠減小服務器須要掃描的數據量
  • 使用索引能夠幫助服務器避免排序和臨時表
  • 使用索引能夠將隨機 I/O 變爲順序 I/O

可是不是全部狀況下,索引都是最好的解決方案,對於很是小的表來講,大部分狀況下簡單的全表掃描更高效,對於中到大型表,索引就比較有效,對於特大型的表來講,分區會更加有效。

Reference

B 樹與 B+ 樹

從B樹、B+樹、B*樹談到R 樹

《算法導論》

《高性能 MySQL》

相關文章
相關標籤/搜索