innoDB索引使用和優化彙總

一,索引類型

最多見的索引數據結構是B-Tree索引,按照順序存儲數據,因此mysql能夠用來作order by和group by操做,應該數據是有序的,因此b-tree也就會將相關的列值都存儲在一塊兒。最後由於索引中存儲實際的列值,某些查詢只使用索引就能夠完成所有查詢。mysql

單列索引

單一的一列索引sql

多列索引

如圖,一個包含三列值的普通索引樹數據結構

聚簇索引

不是一種單獨的索引類型,而是一種數據存儲方式,innoDB的聚簇索引實際上在同一結構中保存了B-Tree索引和數據行,當表有聚簇索引時,它的數據行實際上存放在索引的葉子頁中,不是全部的存儲引擎都支持聚簇索引。函數

聚簇索引和非聚簇索引對比圖:性能

覆蓋索引

若是一個索引包含或說覆蓋全部須要查詢的字段的值,成爲覆蓋索引優化

二,如何正確的使用索引?

索引依據

一、依據where查詢條件創建索引ui

eg:
select a,b from tb_test where c = ?;
idx_c(c)   ->正確

select a,b from tb_test where c = ? and b = ?
idx_cd(c,d)  ->正確

二、根據排序order by ,group by , distinct 字段添加索引編碼

eg:
select * from tb_test order by a;
select a,count(*) from tb_test group by a;
idx_a(a)  ->正確

select * from tb_test order by a,b;
idx_a_b(a,b)  ->正確

select * from tb_test order where c = ? by a;
idx_c_a(c,a)  ->正確

索引使用-最左前綴原理

例如,以表user中的a,b,c三個列創建聯合索引spa

①   全列匹配: select * from user where a = ? and b =? and c = ?;排序

    使用創建索引的三個列的值,精確使用到具體的索引,即便順序不一樣mysql查詢優化器會自動調整where語句的順序(而不是innodb),使之適應索引結構

②    最左前綴匹配: select * from user where a = ? and b =? ;

沒有提供徹底的列值,索引是從左起進行連續匹配的,所以也可以使用利用a,b,c三列創建起來的索引。

③   使用索引精確匹配,中間某個條件未提供。Select * from user where a = ? and c =? ;

雖然a和c都在索引列中,可是由於b不存在,因此沒法匹配最左前綴的鏈接

解決辦法:1,若是有大量的查詢經過這種方式進行,能夠考慮在a和c列上創建一個聯合索引。2,經過填坑的方式,即若是b列上的值很少的話(例如枚舉,或者簡單的bit類型),經過將sql優化成 select * from user where a = ? and b in(?,?,?……) and c = ? 的方式可以提高一部分的性能。

④    查詢沒有使用到索引第一列 select * from user where b = ? and c = ?;

    這種狀況是不符合最左前綴的,沒法使用該索引

⑤   匹配字符串前綴狀況 select * from user where a = ? and b= ? and c like ‘abc%’;

    這種狀況符合最左前綴,可使用索引,但若是通配符("_" "%"等)不是出如今末尾,則沒法使用。

⑥   範圍查詢 select * from user where a > ? and b = ? and c = ?;

這種狀況可以使用索引,可是b和c列的索引沒法使用到,若是範圍查詢不是最左前綴或者查詢條件中有兩個範圍列則沒法使用。

idx_ab(a,b)爲例:

能使用上述索引進行排序的操做是:

order by a;
a = 3 order by b;
order by a,b;
order by a desc ,b desc;
a > 5 order by a;

不能使用索引幫助排序的查詢

order by b; #沒有使用到聯合索引的第一個字段

a > 5 order by b;  #一旦前綴操做是一個range而非=操做,那麼就沒法利用到索引,
這裏 a>5沒法利用索引,二聯合索引的第一個字段未利用,
所以 order by b也沒法利用索引查詢

a in (1,3) order by b; #in裏面的值沒有創建索引,所以沒法利用索引,a未用所以order by b也沒法使用

order by a asc, b desc; #這裏order by a esc是利用了索引,可是b desc未利用到,由於b要和a排序方式一致纔可利用到索引

⑦   條件中帶有函數或者表達式select * from user where a = ? and b = ? and left(c,2) = ‘ba’

雖然和c like ‘ba%’;達到的效果是一致的,可是因爲使用了函數,所以沒法使用索引。

對於使用了表達式的sql,例如 select * from user where a = ? and b = ? and c -1 =?;沒法使用索引。

⑧   字段類型不匹配,可能會致使沒法使用索引 a int(11) ,idx_a(a)

where a = '123' ->錯誤,可能致使未知的錯誤,這個跟編碼有關係

where a = 123 ->正確

三,哪些字段適合建立索引?

一、字段值的重複程度

如身份證號碼基本上不可能重複,所以選擇性很是好,而人的名字重複性較低,選擇性也不錯, 性別(男/女)選擇性較差,重複度很是高

二、選擇性不好的字段一般不適合建立索引,但也有例外

如:男女比例相仿的表中,性別不適合建立單列索引,若是走索引不如走全表掃描,
 由於走索引的I/O開銷更大
    
 但若是男女比例極度不平衡,要查詢的又是少數方,如:理工學校、IT公司等能夠考慮使用索引

三、聯合索引中選擇性好的字段應該排在前面

select * from tab_a where gender=? and name=?
idx_name_gender(name,gender)   ->正確

四、聯合索引能夠爲單列、複列查詢提供幫助

idx_smp(a,b,c)
where a=?;                ->正確
where a=? and b=?;        ->正確
where a=? and c=?;        ->正確 (注:須要MySQL5.6版本以上;在5.5及之前版本,能夠對a字段進行索引掃描,但c字段不行    )
where a=? and b=? and c=? ->正確

五、合理建立聯合索引,避免冗餘

(a),(a,b),(a,b,c)      ->不可取
(a,b,c)                ->正確,能夠覆蓋前兩個

6,合理使用覆蓋索引

對於最核心的SQL,咱們能夠考慮使用索引覆蓋,查詢用戶名這種操做頻率很是高,而索引裏面又存儲了字段的值,查詢時,name字段的值直接在索引中返回,而不須要回表。

覆蓋索引覆蓋就是將你要查詢的字段和條件字段一塊兒創建聯合索引,這樣的好處是不須要回表獲取name字段,IO最小,速度塊

select name from tb_user where userid=?
key idx_uid_name(userid,name)   ->覆蓋索引掃描
相關文章
相關標籤/搜索