重點參考:html
必定要仔細看其中講的索引原理!!!本文中都是簡單的總結。java
參考:mysql
MySQL索引選擇及規則整理:仔細看裏面提到的「前綴索引」segmentfault
整體來講,索引類型只存在:聚簇索引 和 非聚簇索引(二級索引)。
聯合索引
、前綴索引
都是非聚簇索引
中的更明確分類。
覆蓋索引
(我的以爲)並不算一種索引類型,而是基於非聚簇索引
的原理對查詢的一種優化方式。markdown
「回表查詢」:
回到聚簇索引取行數據。1次回表查詢須要2次B+樹的遍歷查找
,因此應該儘可能避免回表(不要刻意避免,以避免得不償失)。函數
爲何InnoDB只有一個聚簇索引,而不將全部索引都使用聚簇索引?
由於「葉子節點中存放了該索引對應的行記錄的完整數據」,若是全部索引都是聚簇索引,意味着每一個葉子節點都保存一份數據,會形成數據的冗餘和資源的浪費。性能
哪些列索引能夠是聚簇索引?
InnoDB中通常都是PK;
若是不存在PK,則會選擇惟一非空索引
代替。
若是不存在惟一非空索引
,則會隱式定義一個PK
來做爲彙集索引。mysql索引
建議向聚簇索引中插入有序的值
例如,聚簇索引列是pk,建議選擇int, auto_increment
,而避免使用無序的UUID
。
a)無序的pk
使數據存儲稀疏,這就會出現聚簇索引有可能有比全表掃面更慢
b)無序的pk
新插入數據時,可能須要插入到某些列的中間,這可能致使數據頁
分裂,從而移動行數據。
c)有序的pk值
很好的避免了上述無序的pk
帶來的問題。
(通常都指的是 單列索引,相對 聯合索引 而言)
爲何叫二級索引
的一種解釋
二級索引須要兩次B+樹的遍歷查找才能取到數據。
第一次經過二級索引找到索引的葉子節點,從而找到數據的主鍵(或者其聚簇索引的索引值),而後用該主鍵去聚簇索引中再次經過B+樹查找到完整的行數據。因此,「回表」會有2次B+樹的查找過程。
爲何輔助索引
使用「聚簇索引的索引值」做爲pointer,而不是使用"地址值"做爲pointer?
使用"地址值"帶來的好處:
1)"地址值"比"聚簇索引的索引值"佔用更少的空間
2)減小了1次B+樹查找的過程。
可是,相應的須要維護輔助索引
,這是一個至關困難的維護工做。
使用「聚簇索引的索引值」做爲pointer時,當出現行移動或者數據頁分裂時,輔助索引不受影響(即不須要維護 輔助索引)
輔助索引
中的最左前綴匹配原則
單列輔助索引遇到<, <=, =, >, >=, between, like(右邊模糊)
能夠用到索引。
假設存在索引(col_1)
,例如liek 'xxx%'
是能夠用到輔助索引的。
屬於輔助索引
,只是:將多列做爲索引,默認多列往右匹配。
聯合索引
中的最左前綴匹配原則
聯合索引遇到範圍查詢時就中止匹配。(待商榷)
假設存在索引(a, b, c, d)
,那麼where a =1 and b = 2 and c > 3 and d = 4
中,a, b
能夠用到聯合索引。此時,建立(a, b, d, c)
索引更合適,而且因爲查詢優化器的優化 where中 a,b,d能夠任意順序。
(擴展疑問:以上聯合索引中,c可否用到索引?參考後面提到的索引下推
)
優化器對單列輔助索引
與聯合索引
的選擇
例如存在單列輔助索引(col_1)
和 聯合索引(col_1, col_2)
,在執行查詢時,優化器是選擇 單列輔助索引 仍是 聯合索引,主要仍是須要結合實際SQL。
where col_1=xxx
,可能會選擇 單列輔助索引。(不肯定,具體仍是看 explain)
where col_1=xxx order by col_2
,選擇 聯合索引,由於col_2
是在col_1
的基礎上排序,避免了進行1次filesort
。
前綴索引能有效減少索引文件的大小,提升索引的速度。
可是前綴索引也有它的壞處:
1)不能在 ORDER BY 或 GROUP BY 中使用前綴索引
2)也不能把它們用做覆蓋索引(Covering Index)。
針對2)
的我的理解,前綴索引的葉子節點記錄的也只是"主鍵"和"前綴值",須要回表才能拿到完整的值。
例如,假設須要建立 article_title列的索引,可是 article_title 可能很長(索引佔用空間多),那麼能夠只取article_title的前N個字符做爲 前綴索引。
語法:CREATE INDEX index_name ON table_name(column_name(length));
InnoDB存儲引擎支持覆蓋索引,即從輔助索引中就能夠獲得查詢的記錄,而不須要查詢彙集索引中的記錄。所以:
1) 使用覆蓋索引能夠避免回表查詢(減小了大量的IO操做)
例如,假設存在索引(col_1, col_2, col_3)
,現有查詢SQL select * from table where col_1 = xx
。若是在需求知足的狀況下,能夠有效利用覆蓋索引來優化查詢SQL select col_1, col_2, col_3 from table where col_1 = xx
。
2) 有助於統計
例如,假設存在非聚簇索引(name)
和聚簇索引(id)
,在執行統計查詢select count(*)
時,查詢優化器可能會選擇使用 非聚簇索引。由於,非聚簇索引 要遠小於 聚簇索引。
暫時還沒法理解2)
,特別是 彙集索引、輔助索引、覆蓋索引、聯合索引 中基於 聯合索引 & count 的示例更不理解~~~
student表:PRIMARY KEY (
id
), KEYidx_name
(name
), KEYidx_school_age
(school
,age
)`執行sql:select count(*) from student
優化器會選用idx_name
這個輔助索引。(具體看 explain)執行SQL:select count(*) from student where age > 10 and age < 15
優化器會選用idx_school_age
這個輔助索引。(具體看 explain)
忽略。
相比單例輔助索引
的最左前綴匹配原則,聯合索引 是從左往右依次比較列。
例如col_1, col_2, col_3, col_4
,先比較col_1
,再比較col_2
,以此類推。
參考:
在前面提到了一個疑問:
where a = 1 and b = 2 and c > 3 and d = 4
在已有聯合索引(a, b, c, d)
時,c/d
可否用到聯合索引?
在主要閱讀的的2篇文章(美團大佬、java知音)都說的是:
最左前綴匹配原則,mysql會一直向右匹配直到遇到範圍查詢(>、<、between、like)就中止匹配。
好比a = 1 and b = 2 and c > 3 and d = 4 若是創建(a,b,c,d)順序的索引,d是用不到索引的,
若是創建(a,b,d,c)的索引則均可以用到,a,b,d的順序能夠任意調整。
其中並未提到c
,並且我的以爲 c&d 均可以用到索引(只是不知道其性能如何)。
針對這疑問,我看到了索引下推
。
例如以上SQL可能有2種執行可能:
1)假設 c&d 都沒用到索引,根據聯合索引查詢到知足 a&b 的條件,而後就回表找到全部行數據,再進行遍歷篩選出c > 3 and d = 4
的數據行。
2)假設 c&d 都用到了索引,那麼最後回表的數據行 必定小於等於 1)
中回表的數據行,這就是mysql的索引下推
mysql默認啓用索引下推,能夠經過變量來修改:
SET optimizer_switch = 'index_condition_pushdown=off';
注意:
a) 索引下推只能用於二級索引。(聚簇索引包含了行數據,這時候索引下推並不會起到減小回表操做的效果)
b) 索引下推通常可用於所求查詢字段(select列)不是/不全是聯合索引的字段,查詢條件爲多條件查詢且查詢條件子句(where/order by)字段全是聯合索引。(沒理解~~)
備註:
我的並不肯定是 c&d 都用到索引,仍是隻有 c 用到索引,d未用到索引。
(ps: cnblog的markdown對於 1.
和-
的解析貌似有錯誤,致使下面的序號是亂的)
索引不必定能提升查詢速度,甚至可能比不存在索引時更慢!
一次查詢只能用到1個索引
若是多列查詢存在多個索引,查詢優化器通常選擇區分度高的索引列。
區分度,簡單公式:count(distinct col) / count(*)。
意味着經過索引列能夠返回更少的rows(回表查詢的行數更少)
具體須要看實際數據,好比假設is_download只存在true/false,當下載完成後將false改成true。
此時實際業務數據是不多存在false,當存在大量查詢false的時候,能夠建立索引。
覆蓋索引擁有更高效率和性能
<>
, !=
, not in
where phone=13800010002
(col_a, col_b)
的聯合索引後,大多狀況下不須要再建立a
索引基於 新增/修改索引 來優化查詢時,不能只看到當前須要優化的SQL,還須要結合該表的其他查詢SQL來綜合分析。
例如,當前待優化sql建立了聯合索引(col_1, col_2, col_3, col_4)
,可是可能另一條sql可能須要聯合索引(col_1, col_2, col_4)
。因此,最終聯合索引(col_1, col_2, col_4, col_3)
更適合。
聯合索引,如何決定其col的順序?
最左前匹配原則&列的區分度 的理解運用,固然還要結合實際SQL。
範圍查詢是否會使用索引(例如 like、between-and、in)?
可使用到索引(但具體仍是要看寫法)。
性別字段是否須要建立索引(十萬級以上的表,只有男/女)?
爲何重複值高的字段不能建索引
mysql千萬級大表,關於性別及年齡字段是否須要加索引?
沒有絕對,要根據實際的數據。
例如1億的數據,其中只有10萬的"男"數據,而且老是查詢少的那部分數據,那麼存在索引的效果更好。
(ps:整理完一看,並無寫或整理出多少東西...但磨磨唧唧也花費了蠻多時間)