MySQL在 5.0版本中引入新特性:索引合并優(yōu)化(Index merge optimization)前标,當(dāng)查詢中單張表可以使用多個索引時坡椒,同時掃描多個索引并將掃描結(jié)果進(jìn)行合并。
該特新主要應(yīng)用于以下三種場景:
1、 對OR語句求并集,如查詢SELECT * FROM TB1 WHERE c1="xxx" OR c2=""xxx"時,如果c1和c2列上分別有索引挣跋,可以按照c1和c2條件進(jìn)行查詢,再將查詢結(jié)果合并(union)操作狞换,得到最終結(jié)果
2避咆、 對AND語句求交集,如查詢SELECT * FROM TB1 WHERE c1="xxx" AND c2=""xxx"時修噪,如果c1和c2列上分別有索引查库,可以按照c1和c2條件進(jìn)行查詢,再將查詢結(jié)果取交集(intersect)操作黄琼,得到最終結(jié)果
3膨报、 對AND和OR組合語句求結(jié)果
該新特性可以在一些場景中大幅度提升查詢性能,但受限于MySQL糟糕的統(tǒng)計信息适荣,也導(dǎo)致很多場景查詢性能極差甚至導(dǎo)致數(shù)據(jù)庫崩潰现柠。
以SELECT * FROM TB1 WHERE c1="xxx" AND c2=""xxx" 為例:
1、 當(dāng)c1列和c2列選擇性較高時弛矛,按照c1和c2條件進(jìn)行查詢性能較高且返回數(shù)據(jù)集較小够吩,再對兩個數(shù)據(jù)量較小的數(shù)據(jù)集求交集的操作成本也較低,最終整個語句查詢高效丈氓;
2周循、 當(dāng)c1列或c2列選擇性較差且統(tǒng)計信息不準(zhǔn)時强法,比如整表數(shù)據(jù)量2000萬,按照c2列條件返回1500萬數(shù)據(jù)湾笛,按照c1列返回1000條數(shù)據(jù)饮怯,此時按照c2列條件進(jìn)行索引掃描+聚集索引查找的操作成本極高(可能是整表掃描的百倍消耗),對1000條數(shù)據(jù)和1500萬數(shù)據(jù)求交集的成本也極高嚎研,最終導(dǎo)致整條SQL需要消耗大量CPU和IO資源且相應(yīng)時間超長蓖墅,而如果值使用c1列的索引,查詢消耗資源較少且性能較高临扮。
由于上述的問題论矾,絕大多數(shù)的運(yùn)維團(tuán)隊都會選擇關(guān)閉該特性來避免執(zhí)行異常,京東商城也出現(xiàn)過類似案例杆勇,嚴(yán)重影響業(yè)務(wù)正常運(yùn)行贪壳。
最近系統(tǒng)中發(fā)現(xiàn)SQL執(zhí)行異常,SQL類似為:
SELECT *
FROM tb_xxxx_xxxx
WHERE yn=0
AND C1=‘123456789’
OR C2=‘123456789’;
表上C1和C2列分別建有索引蚜退,但OR條件導(dǎo)致僅掃描任何一個索引都無法得到滿足條件的全部數(shù)據(jù)闰靴,需要同時掃描兩個索引并對兩個臨時結(jié)果求并集,但由于我們關(guān)閉了Index merge特性钻注,導(dǎo)致執(zhí)行優(yōu)化器只能對表進(jìn)行全表掃描并導(dǎo)致執(zhí)行性能不佳传黄。
該問題的臨時解決辦法為開啟Index merge特性,但存在未知風(fēng)險队寇,因此我們建議修改SQL,將OR操作修改為UNION操作章姓,使得不開啟Index merge特性的情況下語句依然能使用多個索引佳遣,優(yōu)化SQL為:
SELECT *
FROM tb_xxxx_xxxx
WHERE yn=0
AND C1=‘123456789’
UNION ALL
SELECT *
FROM tb_xxxx_xxxx
WHERE yn=0
AND C2=‘123456789’
AND C1<>‘123456789’
PS:
1、在第二個SELECT語句中增加第一個SELECT語句條件的反操作凡伊,從而保證兩個SELECT 語句中沒有重復(fù)數(shù)據(jù)零渐,可以使用UNION ALL來求交集,避免UNION所帶來的排序消耗系忙。
2诵盼、在編寫SQL語句時,需要注意OR條件的書寫银还,
原SQL為:
WHERE yn=0
AND C1=‘123456789’
OR C2=‘123456789’
等價于:
WHERE (yn=0 AND C1=‘123456789’)
OR C2=‘123456789’
而實際需求要求所有返回數(shù)據(jù)滿足yn=0的條件风宁,應(yīng)正確寫為:
WHERE yn=0
AND (C1=‘123456789’
OR C2=‘123456789’)