MySQL 的B+樹索引.


一、B+樹索引概述

索引是應用程序設計和開發的一個重要方面。若索引太多,應用程序的性能可能會受到影響(需維護索引的結構和數據);而索引太少,對查詢性能又會產生影響。

二叉樹,左子樹的鍵值總是小於根的鍵值,右子樹的鍵值總是大於根的鍵值。

平衡二叉樹(AVL樹),任何節點的兩個子樹的高度最大差為 1。平衡二叉樹的查詢速度很快,但是維護一棵平衡二叉樹的代價是非常大的。通常來說,需要 1 次或多次左旋和右旋來得到插入或更新后樹的平衡性。

B+ 樹是為磁盤或其他直接存取輔助設備設計的一種平衡查找樹,B+ 樹中的 B 不是代表二叉(binary),而是代表平衡(balance)。在 B+ 樹中,所有記錄節點都是按鍵值的大小順序存放在同一層的葉子節點上,由各葉子節點指針進行連接,葉子節點之間組成一個雙向鏈表。

B+ 樹索引的本質就是 B+ 樹在數據庫中的實現,但是 B+ 索引在數據庫中有一個特點是高扇出性(數據庫分區),因此在數據庫中,B+ 樹的高度一般都在 2-4 層,這也就是說查找某一鍵值的行記錄時最多只需要 2 到 4 次IO。

數據庫中的 B+ 樹索引可以分為 聚集索引和輔助索引

B+ 樹索引並不能找到一個給定鍵值的具體行。B+ 樹索引能找到的只是被查找數據行所在的頁。然后數據庫通過把頁讀入到內存,再在內存中查找,最后得到要查找的數據。

至於為什么選擇 B+Tree而不是B-Tree,可以參考: 以B-tree和B+Tree的區別來分析 mysql 索引實現

  • B+ 樹的層級更少:相較於 B 樹 B+ 每個非葉子節點存儲的關鍵字數更多,樹的層級更少,所以查詢數據更快;
  • B+ 樹查詢速度更穩定:B+ 所有關鍵字數據地址都存在葉子節點上,即每一個索引實際上距離根節點距離相同,所以每次查找的次數都相同所以查詢速度要比 B 樹更穩定;
  • B+ 樹天然具備排序功能:B+ 樹所有的葉子節點數據構成了一個有序鏈表,在查詢大小區間的數據時候更方便,數據緊密性很高,緩存的命中率也會比 B 樹高。
  • B+ 樹全節點遍歷更快:B+ 樹遍歷整棵樹只需要遍歷所有的葉子節點即可,而不需要像 B 樹一樣需要對每一層進行遍歷,這有利於數據庫做全表掃描。
  • B+ 樹的范圍查找非常方便,這是因為 B+ 樹的葉子節點之間依靠單向指針相連。比如查找范圍為 10<index<16 的節點,我們僅僅需要先找到索引為 10 的節點在哪個葉子子節點上,特別需要指出的是,即使比如 14 節點在另一個葉子節點上,也能通過葉子節點之間的指針快速找到索引。

二、索引創建和查看

索引的創建和刪除可以通過兩種方法,一種是 ALTER TABLE,另一種是 CREATE/DROP INDEX。用戶可以設置對整個列的數據進行索引,也可以只索引一個列的開頭部分數據。

## 添加索引
alter table <table_name> add index <index_name>(column_list);
alter table <table_name> add unique(column_list);
alter table <table_name> add primary key(column_list);
## 刪除索引
alter table <table_name> drop index <index_name>;
alter table <table_name> drop primary key;
## 添加索引
create index <index_name> on <table_name>(column_list);
create unique index <index_name> on <table_name>(column_list);
## 刪除索引
drop index <index_name> on <table_name>;

索引的查看,可以使用命令 SHOW INDEX。

SHOW INDEX FROM <table_name>


Non_unique 表示是否非唯一的索引;Column_name 表示索引列的名稱;Seq_in_index 表示索引中該列的位置;Collation 表示列以什么方式存儲在索引中,可以是 A 或 NULL,B+ 樹索引總是 A,即排序的;Sub_part 是否是列的部分被索引,如果是整個列,則該字段顯示為 NULL;Packed 關鍵字如何被壓縮;Null 是否索引中的列含有 NULL 值;Index_type 索引的類型。

Cardinality 非常關鍵的值,表示索引中唯一值的數目的估計值,優化器會根據這個值來判斷是否使用這個索引。這個值並不是實時更新的,如果需要實時更新 Cardinality 的信息,可以使用 ANALYZE TABLE 命令。建議在非高峰時間,對應用程序下的幾張核心表做 ANALYZE TABLE 操作,這能使優化器和索引更好的工作(除了 ANALYZE TABLE 外,還有 SHOW TABLE STATUS、SHOW INDEX 以及訪問 INFORMATION SCHEMA 架構下的表 TABLES 和 STATISTICS 都會去重新計算 Cardinality 值)。

MySQL 對於主鍵索引的創建會采用臨時表的方式,首先會創建一張帶有主鍵索引的臨時表,然后把原表中數據導入到臨時表,接着刪除原表,最好把臨時表重命名為原表名,這部分操作會導致數據庫不可用,因此建議在創建表的時候就定義好主鍵!

MySQL 對於輔助索引的創建支持 FIC —— Fast Index Creation(快速索引創建)方式,其會對創建索引的表加上一個 S 鎖,不需要建立臨時表。

MySQL 5.6 版本開始支持 Online DDL(在線數據定義)操作,其允許輔助索引創建的同時,還允許其他諸如 INSERT、UPDATE、DELETE 這類 DML 操作,其原理是將 DML 操作日志寫入到一個緩存中,待完成索引創建后再將緩存應用到表上,以此達到數據的一致性,這個緩存的大小由參數 innodb_online_alter_log_max_size 控制,默認的大小為 128MB。

AlTER TABLE <tbl_name> [index_type](index_col_name) 
ALGORITHM [=] {DEFAULT|INPLACE|COPY}
LOCK [=] { DEFAULT| NONE| SHARED| EXCLUSIVE }

ALGORITHM 制定了創建和刪除索引的算法,COPY 選擇創建臨時表的方式;INPLACE 表示創建和刪除索引不需要創建臨時表;DEFAULT 會根據參數 old_alter_table 來判斷是使用 INPLACE 算法還是 COPY 算法,該參數的默認值為 OFF,表示采用 INPLACE 方式。

LOCK 指定了創建和刪除索引的時候添加鎖的情況,NONE 表示不添加任何的鎖;SHARE 表示添加 S 鎖;EXCLUSIVE 表示添加 X 鎖;DEFAULT 會根據並發性執行一個鎖升級的過程,先判斷是否可以使用 NONE 模式,若不能,再判斷是否可以使用 SHARED 模式,否則將使用 EXCLUSIVE 模式。

三、聯合索引

聯合索引是指對表上的多個列進行索引。從本質上來說,聯合索引也是一棵B+ 樹。那么什么時候會使用到聯合索引呢?"WHERE a= xxx and b=xxx" 和 "WHERE a= xxx" 都能使用到聯合索引,但是"WHERE b= xxx"則使用不到這個索引,因為葉子節點上的 b 值是無序的,這也是人們常說的 —— 最左前綴匹配。除此之外,因為聯合索引已經對鍵值進行了排序處理,因此對於索引列的排序操作也能使用到索引。

四、覆蓋索引

覆蓋索引是指從索引中就可以得到查詢的記錄,而不需要查詢聚集索引中的整行記錄的所有信息,因此可以減少大量的 IO 操作。比如只查詢索引列的信息。

對於統計問題而言,在同時存在輔助索引和聚集索引的情況下,InnoDB 存儲引擎會優先使用輔助索引來進行統計,因為輔助索引遠小於聚集索引(輔助索引不需要維護整行記錄的全部信息)。

此外,在通常情況下,諸如(a,b)的聯合索引,一般是不可以選擇列 b 作為查詢條件。但是如果是統計操作,並且是覆蓋索引的,則優化器會進行選擇。

五、其他

當訪問的數據占整個表中數據的蠻大一部分時(一般是20%左右),即使存在可以使用的輔助索引,優化器仍然會選擇通過聚集索引來查找數據,因為順序讀要遠大於離散讀。這是由當前傳統機械硬盤的特性所決定的,即利用順序讀來替換隨機讀的查找。可以使用關鍵字 FORCE INDEX 來強制使用某個索引。

Multi-Range Read 優化是 MySQL 5.6 開始支持的一種索引優化方式,目的是為了減少磁盤的隨機訪問,並且將隨機訪問轉化為較為順序的數據訪問,這對於 IO-bound 類型的 SQL 查詢語句可帶來性能極大的提升,適用於 range、ref、eq_ref 類型的查詢。

Index Condition Pushdown(索引下推) 優化是 MySQL 5.6 開始支持的一種索引優化方式,默認開啟,使用 SET optimizer_switch = 'index_condition_pushdown=off'; 可以將其關閉。ICP 優化可以有效的提高查詢效率,適用於 range、ref、eq_ref、ref_or_null 類型的查詢。

順便提一下 MyISAM 的存儲引擎索引實現,與 InnoDB 不同的是,無論是一級索引,還是二級索引,MyISAM 的索引葉節點的 data 域存放的直接是數據記錄的地址,沒有另外構建聚簇索引。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM