MySQL中的索引 B+Tree
1. 常見索引的數據結構
示意圖均從www.cs.usfca.edu生成
- 哈希(Hash)
- 示意圖
- 效率高,但是因爲Hash算法的特性,數據無序,不能進行範圍搜索
- MySQL支持Hash索引
- 示意圖
- 二分搜索樹(Binary Search Tree)
- 示意圖
- 數據有序,能夠按範圍查找,但是容易造成部分數據傾斜,導致該部分數據查詢較慢
- 示意圖
- 平衡二分搜索樹(Balanced Binary Search Tree, AVL Tree)
- 示意圖
- 是二分搜索樹的優化版,解決了數據傾斜的問題,但是需要維護樹平衡的額外開銷
- 示意圖
- B樹(B Tree)
- 示意圖
- 每個節點可以存多條數據,子節點數量也可以大於2,優化了樹的深度
- 示意圖
- B+樹(B+Tree)
- 示意圖
- 葉子節點會額外存儲指向相鄰葉子節點的指針,優化B樹範圍查詢效率,且更加穩定
- 最底層存實際的數據(只有葉子節點存數據)
- 示意圖
- B*樹
- 是B+樹的變體,在B+樹的非根、非葉子節點再增加指向兄弟節點的指針
2. MySQL中B+Tree的使用
- MySQL中索引主要採用B+Tree(Memory引擎採用Hash索引),其關鍵點如下:
- 非葉子節點存索引,控制樹的深度
- 葉子節點存數據,以及指向相鄰葉子節點的指針
- 數據存入硬盤,需控制每一份數據的大小,以適應硬盤分頁
- 那麼每一份數據應該存多大呢?
- MySQL每一份數據是硬盤分頁大小的倍數
- 系統從硬盤中進行讀取操作,是按頁讀取的
- 如果MySQL每一份數據的大小比硬盤一頁的量小,那麼每次硬盤讀取就可能會讀取一些不需要的數據,影響性能
- 通過
SHOW GLOBAL STATUS LIKE 'Innodb_page_size';
即可查看MySQL每份數據的大小 - MySQL-5.7中Innodb_page_size=16kb
- MySQL每一份數據是硬盤分頁大小的倍數
3. MySQL中兩大主要引擎的索引
- MyISAM(非聚集索引)
- 索引(*.MYI)和數據(*.MYD)分開存
- *.MYI文件中B+Tree的葉子節點存儲數據地址,地址對應*.MYD中的數據
- 主鍵索引(Primary Key): 直接根據索引找到值
- 輔助索引(Secondary Key): 直接根據索引找到值
- 其他
- 支持表鎖
- 不支持外鍵
- 不支持事務
- 不支持全文索引
- 索引(*.MYI)和數據(*.MYD)分開存
- InnoDB(聚集索引)
- 索引和數據存一起(*.ibd)
- 必須有一個索引,沒有會建一個默認的長整型索引(一般推薦設置一個整型自增主鍵,隨機主鍵會導致B+Tree頻繁分裂)
- *.ibd文件中包含B+Tree索引,以及具體的數據(存在B+Tree的葉子節點)
- 主鍵索引(Primary Key): 直接根據索引找到值
- 輔助索引(Secondary Key): 先用索引找到主鍵,再根據主鍵找到值
- 其他
- 支持表鎖、行鎖
- 支持外鍵
- 事務安全
- 支持全文索引
- 索引和數據存一起(*.ibd)
4. 適合創建索引的字段
- 主鍵自動創建索引(推薦自增整型主鍵)
- 頻繁作爲查詢條件的字段
- 經常與其他表關聯的字段
- 經常用作統計、聚合分析的字段
- 經常用作排序的字段
- 單鍵/組合索引,滿足業務條件下,優先選擇組合索引(最左前綴原則)
- 注意點
- 使用索引時,應遵守最左前綴原則
- 不要在索引列上做任何操作(計算、函數等等),否則會導致索引失效
- !=、<>、IS NULL、IS NOT NULL會導致索引列失效
- 關於like查詢,LIKE '%word%'會可能導致索引列失效,LIKE 'word%'仍能使用索引
- 如果where中存在範圍條件,那麼該條件右邊的列的索引會失效
- 儘量使用覆蓋索引(只訪問索引)
- OR可能會導致索引失效