索引使用策略及優化

MySQL的優化主要分爲結構優化(Scheme optimization)和查詢優化(Query optimization)。本章討論的高性能索引策略主要屬於結構優化範疇。本章的內容完全基於上文的理論基礎,實際上一旦理解了索引背後的機制,那麼選擇高性能的策略就變成了純粹的推理,並且可以理解這些策略背後的邏輯。

聯合索引及最左前綴原理

聯合索引(複合索引)
首先介紹一下聯合索引。聯合索引其實很簡單,相對於一般索引只有一個字段,聯合索引可以爲多個字段創建一個索引。它的原理也很簡單,比如,我們在(a,b,c)字段上創建一個聯合索引,則索引記錄會首先按照A字段排序,然後再按照B字段排序然後再是C字段,因此,聯合索引的特點就是:第一個字段一定是有序的,當第一個字段值相等的時候,第二個字段又是有序的,比如下表中當A=2時所有B的值是有序排列的,依次類推,當同一個B值得所有C字段是有序排列的

| A | B | C |

| 1 | 2 | 3 |

| 1 | 4 | 2 |

| 1 | 1 | 4 |

| 2 | 3 | 5 |

| 2 | 4 | 4 |

| 2 | 4 | 6 |

| 2 | 5 | 5 |

其實聯合索引的查找就跟查字典是一樣的,先根據第一個字母查,然後再根據第二個字母查,或者只根據第一個字母查,但是不能跳過第一個字母從第二個字母開始查。這就是所謂的最左前綴原理。

最左前綴原理
我們再來詳細介紹一下聯合索引的查詢。還是上面例子,我們在(a,b,c)字段上建了一個聯合索引,所以這個索引是先按a 再按b 再按c進行排列的,所以:

以下的查詢方式都可以用到索引

select * from table where a=1;

select * from table where a=1 and b=2;

select * from table where a=1 and b=2 and c=3;

上面三個查詢按照 (a ), (a,b ),(a,b,c )的順序都可以利用到索引,這就是最左前綴匹配。

如果查詢語句是:

select * from table where a=1 and c=3; 那麼只會用到索引a。

如果查詢語句是:

select * from table where b=2 and c=3; 因爲沒有用到最左前綴a,所以這個查詢是用戶到索引的。

如果用到了最左前綴,但是順序顛倒會用到索引碼?

select * from table where b=2 and a=1;

select * from table where b=2 and a=1 and c=3;

如果用到了最左前綴而只是顛倒了順序,也是可以用到索引的,因爲mysql查詢優化器會判斷糾正這條sql語句該以什麼樣的順序執行效率最高,最後才生成真正的執行計劃。但我們還是最好按照索引順序來查詢,這樣查詢優化器就不用重新編譯了。

前綴索引
除了聯合索引之外,對mysql來說其實還有一種前綴索引。前綴索引就是用列的前綴代替整個列作爲索引key,當前綴長度合適時,可以做到既使得前綴索引的選擇性接近全列索引,同時因爲索引key變短而減少了索引文件的大小和維護開銷。

一般來說以下情況可以使用前綴索引:

  • 字符串列(varchar,char,text等),需要進行全字段匹配或者前匹配。也就是=‘xxx’ 或者 like ‘xxx%’
  • 字符串本身可能比較長,而且前幾個字符就開始不相同。比如我們對中國人的姓名使用前綴索引就沒啥意義,因爲中國人名字都很短,另外對收件地址使用前綴索引也不是很實用,因爲一方面收件地址一般都是以XX省開頭,也就是說前幾個字符都是差不多的,而且收件地址進行檢索一般都是like ’%xxx%’,不會用到前匹配。相反對外國人的姓名可以使用前綴索引,因爲其字符較長,而且前幾個字符的選擇性比較高。同樣電子郵件也是一個可以使用前綴索引的字段。
  • 前一半字符的索引選擇性就已經接近於全字段的索引選擇性。如果整個字段的長度爲20,索引選擇性爲0.9,而我們對前10個字符建立前綴索引其選擇性也只有0.5,那麼我們需要繼續加大前綴字符的長度,但是這個時候前綴索引的優勢已經不明顯,沒有太大的建前綴索引的必要了。

一些文章中也提到:

MySQL 前綴索引能有效減小索引文件的大小,提高索引的速度。但是前綴索引也有它的壞處:MySQL 不能在 ORDER BY 或 GROUP BY 中使用前綴索引,也不能把它們用作覆蓋索引(Covering Index)。

索引優化策略
最左前綴匹配原則,上面講到了

  • 主鍵外檢一定要建索引
  • 對 where,on,group by,order by 中出現的列使用索引
  • 儘量選擇區分度高的列作爲索引,區分度的公式是count(distinct col)/count(*),表示字段不重複的比例,比例越大我們掃描的記錄數越少,唯一鍵的區分度是1,而一些狀態、性別字段可能在大數據面前區分度就是0
  • 對較小的數據列使用索引,這樣會使索引文件更小,同時內存中也可以裝載更多的索引鍵
  • 索引列不能參與計算,保持列“乾淨”,比如fromunixtime(createtime) = ’2014-05-29’就不能使用到索引,原因很簡單,b+樹中存的都是數據表中的字段值,但進行檢索時,需要把所有元素都應用函數才能比較,顯然成本太大。所以語句應該寫成createtime = unixtimestamp(’2014-05-29’);
  • 爲較長的字符串使用前綴索引
  • 儘量的擴展索引,不要新建索引。比如表中已經有a的索引,現在要加(a,b)的索引,那麼只需要修改原來的索引即可
  • 不要過多創建索引, 權衡索引個數與DML之間關係,DML也就是插入、刪除數據操作。這裏需要權衡一個問題,建立索引的目的是爲了提高查詢效率的,但建立的索引過多,會影響插入、刪除數據的速度,因爲我們修改的表數據,索引也需要進行調整重建
  • 對於like查詢,”%”不要放在前面。
    SELECT * FROMhoudunwangWHEREunameLIKE'後盾%' -- 走索引
    SELECT * FROMhoudunwangWHEREunameLIKE "%後盾%" -- 不走索引
  • 查詢where條件數據類型不匹配也無法使用索引 如字符串與數字比較不使用索引; 
    CREATE TABLE a(vachar(10));
    EXPLAIN SELECT * FROMaWHEREa="1" – 走索引
    EXPLAIN SELECT * FROM a WHERE a=1 – 不走索引

補充

1、可以使用命令mysql> EXPLAIN EXTENDED sql表達式  在使用show warnings來顯示優化後的sql語句

2、數據量比較大的時候,聯接查詢的效率高於嵌套子查詢

3、聯接查詢的時候,實際上mysql是先執行where在執行join ,有區別於傳統的mysql的執行順序 jion > where  

正則表達式不使用索引,這應該很好理解,所以爲什麼在SQL中很難看到regexp關鍵字的原因

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章