博客內容
1.索引數據結構紅黑樹, Hash, B+樹詳解
2.索引是怎樣支撐千萬級表的快速查找
3.如何基於索引B+樹精準建立高性能索引
4.聯合索引底層數據結構又是怎樣的
5.MySQL索引優化最佳實踐
索引底層原理
什麼是索引?
索引是幫助MySQL高效獲取數據的排好序的數據結構
索引數據結構有哪些
- 二叉樹
- 紅黑樹
- Hash表
- B-Tree
select * from t where t.col2 = 89;
當沒有索引時,需要在磁盤中
6次IO
當創建了索引時,如右側二叉樹,只需要2次IO
右側的二叉樹的每個節點中存儲的時每個記錄的地址
什麼是
記錄
?
簡單的說,就是插入的每一行數據元素
這麼看來,二叉樹讀取元素的時間複雜度是O(log n),爲什麼索引內部實現是B+樹,他有什麼缺點
比如說,當創建的主鍵是col1,二叉樹就會成爲這樣的
如果是有序的話,那麼他就和沒有索的時候向磁盤讀取的次數是一樣的
所以,沒有采用這種數據結構
那
紅黑樹
呢,他是可以自選的,不會使其單邊增長,他的使用情況時怎樣的
紅黑樹也叫不完全平衡二叉樹,如果放入的數據多了,那麼樹的高度會很高,查詢的成本就會隨樹的高度增加而增加
如果進行一次千萬級別的查詢 那麼就要向磁盤讀取23次左右,差不多就10秒左右了
那麼Hash表呢
只需要進行一次hash(key),就可以找到在磁盤上的內存地址只需要一次讀取磁盤操作,就可以得到想要的記錄,這是非常快的
既然Hash表查詢操作那麼快,爲什麼MySQL在99%的情況下使用的是B+樹
這個很好理解,如果我們查找的是一個範圍,Hash表就無能爲力了,因爲哈希表的特點就是可以快速的精確查詢,但是不支持範圍查詢
如果是B-Tree呢?
B-Tree:
- 葉節點具有相同的深度
- 葉結點的指針爲空
- 節點中的數據索引從左到右遞增排列
- 一個節點是16K
可以發現同樣的元素,B樹的表示要比完全平衡二叉樹要“矮”,原因在於B樹中的一個節點可以存儲多個元素。
B樹其實就已經是一個不錯的數據結構,用來做索引效果還是不錯的。
那爲啥沒用B樹,而用了B+樹?
如果查詢的是一個範圍,也是相當麻煩的,需要拿出後面的每個節點,沒有解決查詢範圍的查詢
那麼來看看B+樹 :
- 非葉子節點不存儲data,只存儲suoyin,可以放更多的索引
- 葉子節點不存儲指針
順序訪問指針,提高區間訪問的性能
可以看出開,所有的數據在葉子節點上有一份完整的數據,葉子節點上有一份非葉子節點的冗餘
.
我們看一下上面的數據結構,最開始的Hash不支持範圍查詢,二叉樹樹高很高,只有B樹跟B+有的一比。
B樹一個節點可以存儲多個元素,相對於完全平衡二叉樹整體的樹高降低了,磁盤IO效率提高了。
而B+樹是B樹的升級版,只是把非葉子節點冗餘一下,這麼做的好處是爲了提高範圍查找的效率
。
提高了的原因也無非是會有指針指向下一個節點的葉子節點。
小結:到這裏可以總結出來,Mysql選用B+樹這種數據結構作爲索引,可以提高查詢索引時的磁盤IO效率,並且可以提高範圍查詢的效率,並且B+樹裏的元素也是有序的。
那麼,一個B+樹的節點中到底存多少個元素最合適你有了解過麼?
大概三行B+樹就可以存儲2000多萬個索引元素,一個節點16k,非葉子節點大概可存儲1170個索引,葉子節點大概16個索引
一般2~4層,如果數據更多的話,那就分庫分表
數據庫的存儲引擎瞭解嗎
存儲引擎:
- 非聚集索引:MyISAM
- 聚集索引 : Innodb
哦? 說說MyISAM
MyISAM索引文件和數據文件是分離的(非聚集)
創建一個表時,會創建三個文件:
- XXX.firm : 存儲的是標的數據定義相關的信息,
表結構
- XXX.MYD : 存儲的是表中
所有的數據行
- XXX.MYI : 存儲的是表的索引,底層是
B+樹
select * from t where col1 = 49;
那麼就通過三次IO就取到地址0x90,在MYD中查找記錄
說說InnoDB
InnoDB索引實現(聚集):
- 表數據文件 本身就是按B+樹組織的一個索引結構文件
- 聚集索引-------葉節點包含了完整的數據記錄
創建一個表時,會創建兩個文件:
- XXX.firm : 存儲的是標的數據定義相關的信息,表結構
- XXX.idb : 按B+樹組織的一個索引結構文件,數據在索引的葉子節點中
對比兩張B+樹的葉子節點,你就能明白聚集與非聚集的含義
那你說說什麼是聚集索引?
主鍵索引和數據是聚集在葉子節點上,搜索效率是高於非聚集索引的
爲什麼InnoDB表必須有主鍵,並且推薦使用整型的自增型的自增主鍵?
- 如果沒有創建主鍵索引,MySQL內部會選取一個可標識的列作爲主鍵,如果找不到,他會自動生成一個默認的隱藏列,InnoDB的數據元素必須依靠主鍵索引創建
- 如果使用字符串(UUID)比較,先得轉換成ASCII比較,比較慢,使用整型會快很多,如果是自增型,可以直接往後插,效率較快,舉個例子,如果一個葉子節點已經滿了,想再往進插入的時候,
會影響整個表結構,表會分裂
爲什麼
非主鍵索引結構葉子節點存儲的是主鍵值
?
一致性和節省存儲空間
優化
聯合索引的底層存儲結構是什麼樣子
滿足最左前綴匹配原則(a,b,c)先匹配a,如果a相等,匹配b,以此類推。
其實聯合索引的查找就跟查字典是一樣的,先根據第一個字母查,然後再根據第二個字母查,或者只根據第一個字母查,但是不能跳過第一個字母從第二個字母開始查。這就是所謂的最左前綴原理。
詳細說說最左匹配原則
比如聯合索引索引的例子(a,b,c).下面的索引都可以用到:
select * from t where a=1;//(a)
select * from t where a=1 and b=2;//(a,b)
select * from t where a=1 and b=2 and c=3; // (a,b,c)
再比如說:
select * from t where a=1 and c=3;// 只會用到索引a
select * from t where b=1 and c=2;// 沒有用到索引,會查詢到索引,輔助索引查主鍵索引
如果是這樣
select * from table where b=2 and a=1;
select * from table where b=2 and a=1 and c=3;
mysql查詢優化器會判斷糾正這條sql語句該以什麼樣的順序執行效率最高,最後才生成真正的執行計劃。但我們還是最好按照索引順序來查詢,這樣查詢優化器就不用重新編譯了。
聽說過前綴索引嗎?
- 就是列的前綴作爲索引,比如
like 'XXX%'
,但是像like '%xx%
不會用到前綴索引 - MySQL 前綴索引能有效減小索引文件的大小,提高索引的速度。
- 但是前綴索引也有它的壞處:MySQL 不能在
ORDER BY
或GROUP BY
中使用前綴索引,也不能把它們用作覆蓋索引(Covering Index)。
簡單說說你知道的索引優化策略
-
最左前綴匹配原則
-
一定要建主鍵索引
-
對
where,on,group by,order by
中出現的列使用索引 -
儘量選擇
區分度高的列作爲索引
,區分度的公式是count(distinct col)/count(*)
,表示字段不重複的比例,比例越大我們掃描的記錄數越少,唯一鍵的區分度是1,而一些狀態、性別字段可能在大數據面前區分度就是0 -
對較小的數據列使用索引,這樣會使索引文件更小,同時內存中也可以裝載更多的索引鍵
-
索引列不能參與計算,保持列“乾淨”,比如
from_unixtime(create_time) = ’2014-05-29’
就不能使用到索引,原因很簡單,b+樹中存的都是數據表中的字段值
,但這個進行檢索時,需要把所有元素都應用函數才能比較,顯然成本太大
。所以語句應該寫成create_time = unix_timestamp(’2014-05-29’)
-
爲較長的字符串使用前綴索引
-
儘量的擴展索引,不要新建索引。比如表中已經有a的索引,現在要加(a,b)的索引,那麼只需要修改原來的索引即可
-
不要過多創建索引, 權衡索引個數與DML之間關係,DML也就是插入、刪除數據操作。這裏需要權衡一個問題,建立索引的目的是爲了提高查詢效率的,但建立的
索引過多,會影響插入、刪除數據的速度
,因爲我們修改的表數據,索引也需要進行調整重建 -
對於like查詢,"%"不要放在前面
SELECT * FROM t WHERE uname LIKE'後盾%' -- 走索引
SELECT * FROM t WHERE uname LIKE "%後盾%" -- 不走索引·
-- 查詢where條件數據類型不匹配也無法使用索引
- 字符串與數字比較不使用索引
CREATE TABLE a (a char(10));
EXPLAIN SELECT * FROM a WHERE a="1" – 走索引
EXPLAIN SELECT * FROM a WHERE a=1 – 不走索引
- 正則表達式不使用索引,這很好理解,類似前綴索引,所以爲什麼在SQL中很難看到regexp關鍵字的原因