MySQL中in(常量列表)的執行計划


我們在寫sql的時候,經常用到in,in后面跟一堆常量列表,如id。有人說in的效率很高,而有人說很低;有人說in能使用索引,還有人說in不能使用索引。。。
到底是一個怎樣的情況呢?我們分析以下幾種情況
在這之前,我們先了解一下explain的幾種type類型(本次分析即參照type類型),按照性能從高到低:

const:表中的一個記錄的最大值能夠匹配這個查詢(索引可以是主鍵或惟一索引)。因為只有一行,這個值實際就是常數,因為MYSQL先讀這個值然后把它當做常數來對待

eq_ref:在連接中,MYSQL在查詢時,從前面的表中,對每一個記錄的聯合都從表中讀取一個記錄,它在查詢使用了索引為主鍵或惟一鍵的全部時使用

ref:這個連接類型只有在查詢使用了不是惟一或主鍵的鍵或者是這些類型的部分(比如,利用最左邊前綴)時發生。對於之前的表的每一個行聯合,全部記錄都將從表中讀出。這個類型嚴重依賴於根據索引匹配的記錄多少—越少越好

range:這個連接類型使用索引返回一個范圍中的行,比如使用>或<查找東西時發生的情況

index: 這個連接類型對前面的表中的每一個記錄聯合進行完全掃描(比ALL更好,因為索引一般小於表數據)

ALL:這個連接類型對於前面的每一個記錄聯合進行完全掃描,這一般比較糟糕,應該盡量避免

一,in后面只有1個值
1.1 對於主鍵或者唯一索引,那么type=const,這種性能最高,表示表中只有1個記錄能滿足查詢



1.2 對於普通索引、或者聯合主鍵,type=ref


1.3 對於普通字段,type=all,這種性能最差



二,in后面多余1個值,但少於某一個值,這個值具體是多少,之后會揭曉。
這時,不管是主鍵,還是唯一索引,還是普通索引,type=range


但是在這里需要注意的一個特例是:當你的索引的Cardinality屬性比較低時,type=all,意思就是這個索引的區分度很低,建立的意義不大,
這時他的執行計划type=all,mysql認為走這個索引還不如全表掃描:

到底Cardinality處於什么水平時,性能最好?一般認為這個值越接近count(*),性能最好。而對於像性別這種字段,就沒必要加索引了。




三,相對於第二種情況,當in后面的值多於某一個值,會導致掃描全表。這個經驗值我目前不能確定,咨詢過相關DBA,他們也不能給出其經驗值
3.1 test1表總共27條數據,當in后面的值少於8個時,type=range,而當超過8個時,type=all,如下:





3.2 這個t_word_cost表,總共有38244條數據,word_id上有索引,我測試了一把,當in后面不超過5千多或者6千多時,type=range,這是什么意思?
因為令我不解的是,這個值還不確定,它是波動的,我執行了好多次,有時候是5千多,有時候是6千多,或者其他值

通過對test1、t_word_cost表進行測試,我確實沒有找出規律來,我原來妄想,通過大量實踐得出一個經驗值,然后通過經驗值來判斷到底in后面的個數占count(*)百分比多少的時候,能走索引,看來我徒勞了。


既然不能得出經驗值,那我們只有在實際應用環境下具體選擇解決方案了。
譬如我這次操作t_word_cost表,in后面值的個數都超過count(*)了,如果一次性全部寫進in后面,一次查詢所耗時間是30-40s左右!
根據上面我分析的結果,貌似我應該選擇5000作為臨界值,然后分批、多次查詢,這樣性能應該最高。但實際情況是這樣嗎?
通過我在程序中不斷地人肉測試發現,並不是5000耗時最少,選擇2000或者2500時,總體耗時最少,至於為什么,可能與內存、頻繁的數據庫連接有關吧,
因為我們知道,內存、IO都會影響整體性能,所以怎樣平衡這個度需要自己把握。


免責聲明!

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



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