[慢查優化]建索引時注意字段選擇性 & 範圍查詢注意組合索引的字段順序

寫在前面的話:
  1. 以前曾說過「不要求每一個人必定理解 聯表查詢(join/left join/inner join等)時的mysql運算過程」,但對於字段選擇性差意味着什麼,組合索引字段順序意味着什麼,要求每一個人必須瞭解;
  2. 重複上一次的話:把mysql客戶端(如SQLyog,如HeidiSQL)放在桌面上,時不時拿出來 explain 一把,這是一種美德
    • 確保親手查過SQL的執行計劃,必定要注意看執行計劃裏的 possible_keys、key和rows這三個值,讓影響行數儘可能少,保證使用到正確的索引,減小沒必要要的Using temporary/Using filesort;
  3. 不要在選擇性很是差的字段上建索引,緣由參見優化策略A;
  4. 查詢條件裏出現範圍查詢(如A>7,A in (2,3))時,要警戒,不要建了組合索引卻徹底用不上,緣由參見優化策略B;
咱們先回顧一下字段選擇性的基礎知識。

——字段選擇性的基礎知識—— php

引子:什麼字段均可以建索引嗎? html

以下表所示,sort 字段的選擇性很是差,你能夠執行 show index from ads 命令能夠看到 sort 的 Cardinality(散列程度)只有 9,這種字段上本不該該建索引: mysql

Table web

Non_unique sql

Key_name 性能

Seq_in_index 優化

Column_name spa

Collation .net

Cardinality code

Sub_part

Packed

Null

Index_type

Comment

ads

1

sort

1

sort

A

9

\N

\N


BTREE


 
優化策略A:字段選擇性
  • 選擇性較低索引 可能帶來的性能問題
    • 索引選擇性=索引列惟一值/表記錄數;
    • 選擇性越高索引檢索價值越高,消耗系統資源越少;選擇性越低索引檢索價值越低,消耗系統資源越多;
  • 查詢條件含有多個字段時,不要在選擇性很低字段上建立索引
    • 可經過建立組合索引來加強低字段選擇性和避免選擇性很低字段建立索引帶來反作用;
    • 儘可能減小possible_keys,正確索引會提升sql查詢速度,過多索引會增長優化器選擇索引的代價,不要濫用索引;
再回顧組合索引與範圍查詢的業務場景。

——組合索引字段順序與範圍查詢之間的關係——

引子:範圍查詢 city_id in (0,8,10) 能用組合索引 (ads_id,city_id) 嗎?

舉例,

ac 表有一個組合索引(ads_id,city_id)。

那麼以下 ac.city_id IN (0, 8005) 查詢條件能用到 ac表的組合索引(ads_id,city_id) 嗎?

EXPLAIN

SELECT ac.ads_id

FROM ads,  ac

WHERE

      ads.id = ac.ads_id

      AND ac.city_id IN (0, 8005) 

      AND ads.status = 'online'

      AND ac.start_time<UNIX_TIMESTAMP()

      AND ac.end_time>UNIX_TIMESTAMP()

優化策略B:

因爲 mysql 索引是基於 B-Tree 的,因此組合索引有「字段順序」概念。

因此,查詢條件中有 ac.city_id IN (0, 8005),而組合索引是 (ads_id,city_id),則該查詢沒法使用到這個組合索引。

DBA總結道:

組合索引查詢的各類場景
茲有 Index (A,B,C) ——組合索引多字段是有序的,而且是個完整的BTree 索引。
  • 下面條件能夠用上該組合索引查詢:
    • A>5
    • A=5 AND B>6
    • A=5 AND B=6 AND C=7
    • A=5 AND B IN (2,3) AND C>5
  • 下面條件將不能用上組合索引查詢:
    • B>5 ——查詢條件不包含組合索引首列字段
    • B=6 AND C=7 ——查詢條件不包含組合索引首列字段
  • 下面條件將能用上部分組合索引查詢:
    • A>5 AND B=2 ——當範圍查詢使用第一列,查詢條件僅僅能使用第一列
    • A=5 AND B>6 AND C=2 ——範圍查詢使用第二列,查詢條件僅僅能使用前二列
 
組合索引排序的各類場景
茲有組合索引 Index(A,B)。
  • 下面條件能夠用上組合索引排序:
    • ORDER BY A——首列排序
    • A=5 ORDER BY B——第一列過濾後第二列排序
    • ORDER BY A DESC, B DESC——注意,此時兩列以相同順序排序
    • A>5 ORDER BY A——數據檢索和排序都在第一列
  • 下面條件不能用上組合索引排序:
    • ORDER BY B ——排序在索引的第二列
    • A>5 ORDER BY B ——範圍查詢在第一列,排序在第二列
    • A IN(1,2) ORDER BY B ——理由同上
    • ORDER BY A ASC, B DESC ——注意,此時兩列以不一樣順序排序
 
順着組合索引怎麼建繼續往下延伸,請各位注意「索引合併」概念:
  • MySQL 5,0如下版本,SQL查詢時,一張表只能用一個索引(use at most only one index for each referenced table),
  • 從 MySQL 5.0開始,引入了 index merge 概 念,包括 Index Merge Union Access Algorithm(多個索引並集訪問),包括Index Merge Intersection Access Algorithm(多個索引交集訪問),能夠在一個SQL查詢裏用到一張表裏的多個索引。
  • MySQL 在5.6.7以前,使用 index merge 有一個重要的前提條件:沒有 range 可使用。[出自參考資源2]
索引合併的簡單說明:
  • MySQL 索引合併能使用多個索引
    • SELECT * FROM TB WHERE A=5 AND B=6
      • 能分別使用索引(A) 和 (B) 或 索引合併;
      • 建立組合索引(A,B) 更好;
    • SELECT * FROM TB WHERE A=5 OR B=6
      • 能分別使用索引(A) 和 (B) 或 索引合併;
      • 組合索引(A,B)不能用於此查詢,分別建立索引(A) 和 (B)會更好;
最後的總結:
仍然是強調再強調:
記住,explain 後再提測是一種美德!
參考資源:
1)中文譯稿,2013, MySQL 索引最佳實踐之問題反饋
2)orczhou,2013, MySQL優化器:index merge介紹
3)orczhou,2013, index merge的補充說明
5)nocode,2013, MySQL Internals-Index Merge優化
 
贈圖1枚:
轉:喵了個咪的,老闆催我去重構遺留的 Python 代碼……
http://ww2.sinaimg.cn/bmiddle/7cc829d3jw1e8qihxzpebg20af07yx6p.gif
相關文章
相關標籤/搜索