摘要
IN 一定走索引嗎?那當然了,不走索引還能全部掃描嗎?好像之前有看到過什么Exist,IN走不走索引的討論。首先說明:IN肯定會走索引,但是當IN的取值范圍較大時會導致索引失效,走全表掃描。
我就在我本地找一找張之前隨便建的表,來看一下:
CREATE TABLE `products` ( `id` INT(10) NOT NULL AUTO_INCREMENT, `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, `code` VARCHAR(255) NOT NULL DEFAULT '' COLLATE 'utf8mb4_general_ci', `price` INT(10) NOT NULL DEFAULT '0', PRIMARY KEY (`id`) USING BTREE ) COLLATE='utf8mb4_general_ci' ENGINE=InnoDB AUTO_INCREMENT=47 ;
然后插入一些數據,從上面的建表語句,可以看到,我已經插入了46條數據。
EXPLAN 分析執行的SQL語句,當然也可以使用工具提供的快捷鍵,這里,我使用的是HeidiSQL 這款工具
場景1:當IN中的取值只有一個主鍵時
我們只需要注意一個最重要的type 的信息很明顯的提現是否用到索引:
type結果值從好到壞依次是:
system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL
對上面的解釋:
all:全表掃描
index:另一種形式的全表掃描,只不過他的掃描方式是按照索引的順序
range:有范圍的索引掃描,相對於index的全表掃描,他有范圍限制,因此要優於index
ref: 查找條件列使用了索引而且不為主鍵和unique。其實,意思就是雖然使用了索引,但該索引列的值並不唯一,有重復。這樣即使使用索引快速查找到了第一條數據,仍然不能停止,要進行目標值附近的小范圍掃描。但它的好處是它並不需要掃全表,因為索引是有序的,即便有重復值,也是在一個非常小的范圍內掃描。
const:通常情況下,如果將一個主鍵放置到where后面作為條件查詢,mysql優化器就能把這次查詢優化轉化為一個常量。至於如何轉化以及何時轉化,這個取決於優化器
一般來說,得保證查詢至少達到range級別,最好能達到ref,type出現index和all時,表示走的是全表掃描沒有走索引,效率低下,這時需要對sql進行調優。
當extra出現Using filesor或Using temproary時,表示無法使用索引,必須盡快做優化。
possible_keys:sql所用到的索引
key:顯示MySQL實際決定使用的鍵(索引)。如果沒有選擇索引,鍵是NULL
rows: 顯示MySQL認為它執行查詢時必須檢查的行數。
場景2:擴大IN中的取值范圍
此時仍然走了索引,但是效率降低了
場景3:繼續擴大IN的取值范圍
發現此時已經沒有走索引了,而是全表掃描,type 的值為ALL ,並且我共插入了46條數據,這里檢查的rows 就是46,也恰巧證明了是全表掃描
場景4:表中存在多個索引
在一張表中,僅有千萬級別的數據,現在我有一個SQL語句,我該增加的索引都增加了,但是執行速度很慢,我們經過分析執行的SQL語句得到如下:
是因為,在查詢的時候,使用的索引錯誤了,也可以強制其走指定的索引:
select * from table force index(idx_start_date) where ....
總結
根據實際的情況,需要控制IN查詢的范圍。原因有以下幾點
1. IN 的條件過多,會導致索引失效,走索引掃描
2. IN 的條件過多,返回的數據會很多,可能會導致應用堆內內存溢出。
所以必須要控制好IN的查詢個數