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


寫在前面的話:
  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;

我們先回顧一下字段選擇性的基礎知識。

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

引子:什么字段都可以建索引嗎?

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

Table

Non_unique

Key_name

Seq_in_index

Column_name

Collation

Cardinality

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM