一、B-Tree 簡介
BTree 是一種多路搜索樹
- 定義任意非葉子節點最多只有M個兒子 , M> 2
- 根節點的兒子數為 [2,M]
- 除根節點外的非葉子節點的兒子數為 [M/2,M]
- 每個節點存放至少 M/2-1 (向上取整) 和 至多M-1 個關鍵字 (至少2個關鍵字)
- 非葉子節點的關鍵字個數 = 指向兒子的指針個數 -1
- 非葉子節點的關鍵字: K[1]、K[2] ...K[M-1] , 且 K[i] < K[i+1]
- 非葉子節點的指針:P[1]、P[2] ...P[M] ,其中 P[1] 指向關鍵字小於 K[1] 的子樹,P[M] 指向關鍵字大於 K[M-1] 的子樹
- 所有葉子節點位於同一層
特點:
- 關鍵字集合分布在整棵樹中
- 任何一個關鍵字出現且只出現在一個節點中
- 搜索有可能在非葉子節點結束
- 其搜索性能等價於關鍵字全集內做一次二分查找
- 自動層次控制
B-Tree 的搜索,從根節點開始,對節點內的關鍵字(有序)進行二分查找,如果命中則結束,否則進入查詢關鍵字范圍的兒子節點。重復直到對應的兒子指針為空,或者已經是葉子節點
二、B+Tree
B+Tree 是 B-Tree 的變體,也是一種多路搜索樹
- 非葉子節點的子樹指針與關鍵字個數相同
- 非葉子節點的子樹指針 p[i] ,指向關鍵字值屬於 (K[i] ,K[i+1]) 的子樹
- 所有的葉子節點增加一個鏈指針
- 所有關鍵字都在葉子節點出現
特點:
- 所有關鍵字都出現在葉子節點的鏈表,且鏈表中的關鍵字恰好是有序的
- 不可能在非葉子節點命中
- 非葉子節點相當於葉子節點的索引,葉子節點相當於是存儲關鍵字數據的數據層
- 更適合文件索引系統
B+Tree因為非葉子節點不存放數據,所以相對於B-Tree可以存放更多的鍵與指針,讓樹變得更矮,這樣降低了I/O操作
不同的存儲引擎對索引有不同的支持:Innodb 和 MyISAM 默認的索引是Btree , 而 memory 默認的索引是 Hash 索引
三、聚簇索引與非聚簇索引
聚簇索引
聚簇索引就是指主索引文件和數據文件為同一份文件,聚簇索引主要用在Innodb 存儲引擎中。在該索引實現方式中 B+Tree 的葉子節點上的 data 就是數據本身,key 為主鍵。
如果是一般索引的話,data 便會指向對應的主索引在B+Tree 的每個葉子節點增加一個指向相鄰葉子節點的指針,就形成了帶有順序訪問指針的 B+Tree ,這樣做的目的是為了
提高區間訪問的性能
非聚簇索引
非聚簇索引就是指B+Tree的葉子節點上的data ,並不是數據本身,而是數據存放的地址。主索引和輔助索引沒有區別,只要主索引中的 key 一定得是唯一的,主要用在 MyISAM
存儲引擎中。非聚簇索引比聚簇索引多了一次讀取數據的IO 操作,所以查找的性能會差
四、哈希索引
哈西算法的時間復雜度為O(1) ,哈希表也稱為散列表,在哈希的方式下,一個元素 k 處於 h(k) 中,即利用哈希數 h ,根據關鍵字 k 計算出槽的位置,函數 h 將關鍵字映射到哈希表 T[0 ...m-1] 的槽位上
上圖中哈希函數 h 可能將兩個不同的關鍵字映射到相同的位置,這叫碰撞,在數據庫中一般采用鏈接法解決,在鏈接法中,將散列到同一個槽位的元素放在一個鏈表中,如下圖
BTree 索引與 Hash 索引的區別
Hash 索引結構特殊,檢索效率非常高,索引可以一次定位,不像 BTree 索引需要從根節點到葉子節點,所以Hash 索引的查詢效率要遠高於B-Tree 索引。而數據庫默認存儲引擎 Innode 默認的索引卻是
B+Tree ,因為 Hash 索引特殊也帶來了很多限制與弊端
- Hash 索引僅僅能夠滿足 “=” ,“IN” , “<=>” 查詢,不能使用范圍查詢,如 where age > 20,因為 Hash 索引是計算Hash運算后,處理后的值不能保證與原址的大小關系
- Hash 索引無法被用來避免數據的排序操作,因為同上,計算后的值無法利用索引的數據來避免任何排序運算
- Hash 索引在任何時候都不能避免表掃,可能存在不同的key 計算出相同的hash 值,所以即使滿足hash 查詢,也需要全表掃描
- Hash 索引遇到大量Hash 值相同的情況下性能會急劇下降
參考:https://www.cnblogs.com/igoodful/p/9361500.html
五、單一索引與復合索引
看了一篇文章,實際測試了索引是否生效,個人覺得最深的一句話是所有是否生效以及執行情況需要在具體的數據庫版本與存儲引擎下來將。本人將測試如下:
數據庫版本:8.0.21
5.1 聯合索引測試
創建表t_user,以及聯合索引
CREATE TABLE `t_user` ( `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, `user_id` varchar(20), `user_name` varchar(20), `password` varchar(20), `address` varchar(255), PRIMARY KEY (`id`) USING BTREE, INDEX `聯合索引`(`user_id`,`user_name`,`password`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4;
1.條件 user_id , 使用了聯合索引
2.條件user_name,沒有進行索引查詢
3.條件 password,也沒有進行索引查詢
4.組合查詢:user_id and user_name ,依然走索引
5.將 and 換位 or , 也走索引
更換順序,將 user_name 放前面。也是走索引的
6.條件 user_name,password,不走索引
7.條件 user_id,user_name,password ,走索引
8. 條件 user_id,password ,走索引
例如 (a,b,c)復合索引,等價於(a),(a,b),(a,b,c)這樣的類型,如果查詢(b),(c) ,(bc)是不走索引的,唯一與預期不復合的是 or 查詢,也會走索引,與字段順序無關。
5.1 單列索引測試
CREATE TABLE `t_user` ( `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, `user_id` varchar(20), `user_name` varchar(20), `password` varchar(20), `address` varchar(255), PRIMARY KEY (`id`) USING BTREE, INDEX `用戶id`(`user_id`) USING BTREE, INDEX `用戶名稱`(`user_name`) USING BTREE, INDEX `密碼`(`password`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4;
1.條件 user_id ,走用戶id 索引
2.條件 user_name,走索引
3.條件 password,走索引
4. 復合查詢 user_id and user_name
將 and 換為 or
5. 復合查詢 user_id,user_name,password
將 and 換為 or
單列索引:如果使用 or 查詢不使用 索引,and 查詢直走第一個參數的索引
此處解釋 explain 執行計划各個字段的含義:
id :選擇標識符
select_type : 查詢的類型
table : 結果集的表
partitions : 匹配的分區
type : 連接的類型
possible_keys : 查詢時可能使用的索引
key:實際使用的索引
row : 掃描出的行數
filtered : 按表條件過濾的百分比
extra: 執行情況的描述和說明
參考:https://www.cnblogs.com/tufujie/p/9413852.html
參考:https://blog.csdn.net/Abysscarry/article/details/80792876
六、索引類型
唯一索引 Unique :不允許重復的索引
普通索引 Normal :提高性能,普通索引
全文索引Fulltext :使用長文本
空間索引 Spatial : 用戶空間類型
唯一索引的查詢性能要大於普通index
七、不走索引的情況
1.單列索引
索引列 a ,b 普通列 c
a and b : 走一個索引,由sql 執行優化器決定
a or b : 不走索引
a and c : 走索引
a or c : 不走索引
結果: 單列索引,條件包含 or 即不執行索引
大於 、小於 、不等於、in 區間判斷
大於號:走索引
小於號:走索引
不等於:走索引
in :走索引
in 多個字段也走索引(有博客說多個參數不走索引,實測走)
like %開頭不走索引,非 %開頭的會走索引
between and 走索引
左側有表達式不走索引
右側計算走索引
null 值判斷 is null 走索引。 = null 不走
字符類型與數值類型比較:不走索引
substring 計算字符不走索引
有博客說:最左匹配原則,第一個條件不走索引,后面的都不走,實際上(age 普通字段,user_name索引字段 )還是會走索引
單列索引總結
使用到索引的情況:
- 大於號
- 小於號
- 等於
- 不等
- like 非%開頭
- in (參數不限多少都走)
- not in
- 條件表達式右側計算 age = 10/2 或者 age = 10/2 + 1
- between and
- is null
未走索引的情況:
- like % 開頭
- 條件左側有計算 age /2 = 10 或者 age+1 = 10
- = null
- 字符類型與數值類型比較
- substring
- or 不走索引
2.復合索引
復合索引列:user_id ,user_name,password
第一列大於小於號走索引
第二列、第三列比較不走索引
第一列不等於走索引
第一列 in 走索引
第一列 between and 走索引
包含or 即不走索引