1. 程式人生 > >MySQL優化(5):索引失效分析、in與exists使用場合

MySQL優化(5):索引失效分析、in與exists使用場合

有一個 來替 null 決定 index idt class 分布 family

一、索引失效的情況

  前文提及過可以通過explain的possible_keys、key屬性判斷索引是否失效,key如果為null,可能是索引沒建,也可能是索引失效,下面列舉一些會使索引失效的情況。

1、全值匹配:順序、個數與索引一致

2、最佳左前綴法則:查詢從索引的最左前列開始並且不跳過索引中的列,中間跳過的值,後面的索引會失效

3、索引列上做了操作(計算、函數、自動或手動類型轉換),會導致索引失效而轉向全表掃描

4、存儲引擎不能使用索引中範圍條件右邊的列

  技術分享圖片

  name字段用於查找,age>11也用到了,但著重用於排序,pos則沒用到索引

5、盡量使用覆蓋索引(索引列和查詢列一致),減少select *

  using where是在表裏檢索,using index會直接從索引裏檢索

  技術分享圖片

  這裏也是範圍檢索,但與上面不同的是這裏從索引裏獲取數據,沒有用到age

  技術分享圖片

6、mysql在使用不等於(!= 或 <>)時無法使用索引

  技術分享圖片

7、is null,is not null也無法使用索引

8、like以通配符開頭(‘%abc..’)也會導致索引失效

  通過覆蓋索引可以解決like ‘%字符串%‘索引失效的問題

  例:假設以name,age字段建索引

create index idx_user_nameAge on tb_user(name,age);

  查詢字段只要有一個和覆蓋索引沾邊就行

  技術分享圖片

  技術分享圖片

  但如果有超過索引的部分,索引就用不上了,所以用select * 就不能使用覆蓋索引

  技術分享圖片

9、字符串不加單引號,該字段以後的索引失效

10、少用or,用它來連接時會索引失效

  技術分享圖片

11、少數據值的列也不應該增加索引,只有兩種情況,且平均分布,加了索引反而降低速度

12、range的包含範圍有一定的閾值,超過會進行全文掃描

二、in與exists使用場合

  堅持小表驅動大表的原則

in:當B表的數據集必須小於A表的數據集時,in優於exists

select * from A where id in (select id from B)
#等價於:
  for select
id from B   for select * from A where A.id = B.id

exists:當A表的數據集小於B表的數據集時,exists優於in

  將主查詢A的數據,放到子查詢B中做條件驗證,根據驗證結果(true或false)來決定主查詢的數據是否保留

  子查詢也可以用條件表達式、其他子查詢或join來替代,何種最優需具體問題具體分析

select * from A where exists (select 1 from B where B.id = A.id)
#等價於
    for select * from A
    for select * from B where B.id = A.id
#A表與B表的ID字段應建立索引

三、對Order By的優化

1、用order by子句的重點是是否會產生filesort。建索引時已經排好序,所以order by的順序和索引最好一致,避免再一次排序。 

  技術分享圖片

  所建的索引默認升序,一升序一降序會產生內排序

  技術分享圖片

2、狀態最好是using index,讓mysql通過掃描索引本身完成排序。

  能使用index方式排序的情況:order by語句使用索引最左前列,或where子句與order子句條件組合滿足索引最左前列。

 (1)order by語句使用索引最左前列,order by後字段同為asc或desc都行

   技術分享圖片

 (2)加上where子句的條件與order by子句條件列組合滿足索引最左前列

  技術分享圖片

 (3)不能使用索引的情況

  技術分享圖片

  假如以category_id、comments、views的順序建索引

  技術分享圖片

3、filesort的兩種算法

 (1)雙路排序:兩次掃描磁盤(讀取行指針和order by列,對他們進行排序,然後掃描已排好序的列表,重新列表讀取數據輸出)。

 (2)單路排序:mysql4.1版本後,從磁盤讀取查詢需要的所有列,按order by列在buffer對它們排序,然後掃描排序後的列表輸出,只讀取一次數據,且把隨機IO變為順序IO,但會使用更多空間,因為它把每一行都保存在內存中。

    單路排序存在的問題:

    因為要把所有字段取出,可能要取出的大小超出sort_buffer容量,導致每次只能取sort_buffer容量大小的數據進行排序(創建tmp文件,多路合並),排完再取sort_buffer容量大小的數據,反而會導致更多I/O操作。

4、order by優化策略:

 (1)單路多路算法的數據都有可能超過sort_buffer_size,超出後會建tmp文件進行合並排序,導致多次I/O,可以根據系統能力增大sort_buffer_size參數設置

 (2)增大max_length_for_sort_data參數,會增加用單路排序的概率,但如果設太大,也會更容易使數據超過sort_buffer_size,當query的字段大小總和小於max_length_for_sort_data且排序字段不是text/blob類型時,才會用單路排序,否則還是用多路排序。

 (3)order by時不要用select *,只select需要的字段,多余的字段會占用sort_buffer的內存

5、group by: 

  適用order by原則,實質先排序後分組,遵守索引建的最佳左前綴,使用不當會產生臨時表。

  技術分享圖片

  當無法使用索引列,增大max_length_for_sort_data和sort_buffer_size參數設置。能在where的條件就不放在having裏。

四、案例,其他註意點

  假如以c1,c2,c3,c4的順序建立索引

1、對於常量類型,查詢優化器會自動調優SQL,順序不影響

   技術分享圖片

2、範圍之後全失效,但查詢優化器會先常量類型自動調優,c3被調前,c4後的失效,但c4是最後一個了,所以仍用到4個。以上的例子中間並沒有斷

   技術分享圖片

3、都只用到了c1,c2,第三條語句無法使用到索引排序,所以mysql內部自己進行了一次排序(前兩個c3沒用到查找,但用到了排序,所以無using filesort,只是沒有記錄到key_len裏)

  技術分享圖片

4、order by不按索引順序會出現using filesort,本來照理第一個按order by c3,c2排序會出現filesort,但是前面已經有c2=‘a2’的條件,c2已經是常量值,所以c2其實不用排序

  技術分享圖片

五、多表連接在從表加索引可以提高速度

案例1:兩表連接的情況,多表連接時在主表還是從表建索引的問題

  如未使用索引的情況

  技術分享圖片

  左連接把索引建在從表的關聯字段比較好,主表一定會有,從表才是檢索的關鍵

  技術分享圖片

案例2:三表關聯要建在哪些字段上

  沒建索引的時候

  技術分享圖片

  在第二、三個從表的關聯字段加索引

ALTER TABLE `phone` ADD INDEX z(`card`);
ALTER TABLE `book` ADD INDEX Y(`card`); 

  技術分享圖片  

結論:

  (1)join語句中被驅動表上join條件字段加索引可以提高效率;

  (2)當無法保證被驅動表的join條件字段被索引且內存資源充足的前提下,不要太吝嗇JoinBuffer的設置。

MySQL優化(5):索引失效分析、in與exists使用場合