where條件順序幾乎不影響索引使用,真正起作用的是最左前綴匹配、數(shù)據(jù)選擇性及是否發(fā)生隱式轉(zhuǎn)換或函數(shù)包裹;執(zhí)行計(jì)劃需重點(diǎn)看type、possible_keys、rows和extra字段。
WHERE 條件順序真的影響索引使用嗎?
MySQL 和 PostgreSQL 等主流數(shù)據(jù)庫(kù)里,WHERE 子句中條件的書寫順序 幾乎不影響 優(yōu)化器是否能用上索引。真正起作用的是列是否出現(xiàn)在索引的最左前綴中、數(shù)據(jù)選擇性高低、以及是否有隱式類型轉(zhuǎn)換。
常見(jiàn)錯(cuò)誤現(xiàn)象:WHERE status = 'active' AND user_id = 123 寫成 WHERE user_id = 123 AND status = 'active',有人以為后者更快——其實(shí)只要 (user_id, status) 是聯(lián)合索引,兩種寫法執(zhí)行計(jì)劃通常完全一致。
- 優(yōu)化器會(huì)重排謂詞順序,按統(tǒng)計(jì)信息和索引結(jié)構(gòu)決定執(zhí)行路徑
- 唯一例外是某些老版本 MySQL(5.6 之前)在極簡(jiǎn)查詢中可能不重排,但已不是現(xiàn)實(shí)問(wèn)題
- 如果你發(fā)現(xiàn)順序變了導(dǎo)致索引失效,大概率是某一邊發(fā)生了隱式轉(zhuǎn)換或函數(shù)包裹,比如
WHERE DATE(created_at) = '2024-01-01'直接讓索引失效,跟順序無(wú)關(guān)
怎么看執(zhí)行計(jì)劃里索引到底有沒(méi)有被用上?
別只盯著 key 字段是否非 NULL,重點(diǎn)看 type、possible_keys、rows 和 Extra 四個(gè)字段。
-
type是關(guān)鍵:出現(xiàn)ALL表示全表掃描;range或ref才算走了索引;index是索引全掃描,不一定高效 -
possible_keys列出所有可用索引,但實(shí)際只選一個(gè);如果為空,說(shuō)明沒(méi)有索引覆蓋 WHERE 條件 -
rows是優(yōu)化器估算的掃描行數(shù),數(shù)字越大越危險(xiǎn)(注意:不是返回行數(shù)) -
Extra里出現(xiàn)Using where是正常;但Using filesort或Using temporary往往意味著排序/分組沒(méi)走索引
示例(MySQL):
EXPLAIN SELECT * FROM orders WHERE user_id = 123 AND status =` `'paid'``;
如果 key 顯示 idx_user_status,type 是 ref,rows 是個(gè)位數(shù),基本沒(méi)問(wèn)題。
聯(lián)合索引最左前綴原則怎么實(shí)操驗(yàn)證?
不是“從左到右必須連續(xù)”,而是“匹配從最左列開始的連續(xù)列”。比如索引是 (a, b, c):
- ?
WHERE a = 1—— 走索引 - ?
WHERE a = 1 AND b = 2—— 走索引 - ?
WHERE a = 1 AND b = 2 AND c > 3—— 走索引(c 用范圍,b 必須等值) - ?
WHERE b = 2—— 不走索引(跳過(guò) a) - ?
WHERE a > 1 AND c = 3—— c 不會(huì)生效(a 是范圍,中斷前綴)
容易踩的坑:
- 在
IN后面跟大量值(如a IN (1,2,3,...1000)),部分版本可能退化為全索引掃描,rows會(huì)暴增 -
OR連接不同列(WHERE a = 1 OR b = 2)通常無(wú)法利用聯(lián)合索引,除非每個(gè)分支都有獨(dú)立索引
為什么加了索引,EXPLAIN 卻顯示沒(méi)用?
索引存在 ≠ 查詢一定用它。優(yōu)化器可能主動(dòng)放棄索引,常見(jiàn)原因:
- 表太?。ū热缰挥袔资校?,走索引反而比全表掃描更慢
- 查詢返回大量數(shù)據(jù)(如
WHERE status != 'cancelled'),優(yōu)化器預(yù)估用索引要回表太多次,直接全表更省 - 索引列上有函數(shù)或表達(dá)式:
WHERE UPPER(name) = 'JOHN'、WHERE age + 1 = 30都會(huì)讓索引失效 - 統(tǒng)計(jì)信息過(guò)期(尤其 ANALYZE TABLE 沒(méi)跑過(guò)),優(yōu)化器誤判選擇性,可通過(guò)
ANALYZE TABLE table_name更新
一個(gè)真實(shí)例子:某張 20 萬(wàn)行的訂單表,加了 (status, created_at) 索引,但 WHERE status IN ('pending', 'processing') 仍走全表——因?yàn)檫@兩個(gè)狀態(tài)占了 95% 數(shù)據(jù),優(yōu)化器認(rèn)為回表成本太高,不如掃一遍聚簇索引。
xiaobangwx-szs.swatchsz.com
aibiwx-szs.swatchsz.com
yadianwx-szs.swatchsz.com
lichamierwx-szs.swatchsz.com
langgewx-szs.swatchsz.com
luojiedubiwx-szs.swatchsz.com
yakedeluowx-szs.swatchsz.com
yubowx-szs.swatchsz.com
zhenlishiwx-szs.swatchsz.com
xiangnaierwx-szs.swatchsz.com
zhibowx-szs.swatchsz.com
diduowx-szs.swatchsz.com
haolishiwx-szs.swatchsz.com
oumijiawx-szs.swatchsz.com
baopowx-szs.swatchsz.com
復(fù)雜點(diǎn)在于:同一 SQL,在不同數(shù)據(jù)分布下,執(zhí)行計(jì)劃可能完全不同。上線前一定要用接近生產(chǎn)的數(shù)據(jù)量做 EXPLAIN 驗(yàn)證,而不是只在空庫(kù)或測(cè)試庫(kù)里看。