MySQL5.0和更新版本中引入了一種叫:索引合並(Index merge)的策略,一定程度上可以使用表上多個單列索引來定位指定的行。
該特性主要應用於以下三種場景:
1. 對or語句求並集,如查詢select * from film_actor where c1 = "xxx" or c2 = "xxx"時,如果c1和c2列上分別有索引,可以按照c1和c2條件進行查詢,再將查詢結果合並(union)操作,得到最終結果。
2. 對and語句求交集,如查詢select * from film_actor where c1 = "xxx" and c2 = "xxx"時,如果c1和c2列上分別有索引,可以按照c1和c2條件進行查詢,再將查詢結果取交集(intersect)操作,得到最終結果。
3. 組合前兩種情況的合並及相交。
該新特性可以在一些場景中大幅度提升查詢性能,但受限於MySQL糟糕的統計信息,也導致很多查詢場景查詢性能極差甚至導致數據庫崩潰。
以select * from film_actor where c1 = "xxx" and c2 = "xxx"為例:
1. 當c1列和c2列選擇性較高時,按照c1和c2條件進行查詢性能高且返回數據集較小,再對兩個數據量較小的數據集求交集的操作成本也比較低,最終整個語句查詢高效;
2. 當c1列或c2列選擇性較差且統計信息不准時,比如整表數據量1000萬,按照c2列條件返回800萬數據,按照c1列返回100條數據,此時按照c2列條件進行索引掃描+聚集索引查詢的操作成本極高(可能是整表掃描的百倍消耗),對100
條數據和800萬數據求交集的成本也極高,最終導致整條SQL需要消耗大量CPU和IO資源,且相應時間超長,而如果值使用c1列的索引,查詢消耗資源少且性能較高。
即使用select * from film_actor where c1 = "xxx" union all select * from film_actor where c2 = "xxx"往往更好。
索引合並策略有時候是一種優化的結果,但實際上更多的時候說明了表上的索引建的的很糟糕:
1、當出現服務器對多個索引做相交操作時(通常有多個and條件),通常意味着需要一個包含所有相關列的多列索引,而不是多個獨立的單列索引。
2、當服務器需要對多個索引做合並操作時(通常有多個or條件),通常需要消耗大量cpu和內存資源在算法緩存、排序和合並操作上。特別是當其中某些索引的選擇性不高,需要合並掃描返回的大量數據的時候。
3、更重要的是,優化器不會把這些計算到“查詢成本”中,優化器只關心隨機頁面讀取。這回使得查詢的成本被“低估”,導致該執行計划還不如直接走全表掃描。