MySQL中in到底走不走索引?

explain介紹
mysql中explain關鍵字可以模擬MySQL優化器執行SQL語句,是一個可以很好的分析SQL語句或表結構的性能瓶頸。

explain的使用方法:explain + sql語句,下面我們先來執行下explain語句

EXPLAIN SELECT * FROM `user` WHERE created_time > "2020-03-08";
執行結果如下:

 

可以看到有幾個返回參數:id、select_type、table、partitions、type、possible_keys、key、key_len、ref、rows、filtererd、Extra。

下面先介紹下這些參數的含義

id // 選擇標識符
select_type // 表示查詢的類型
table // 輸出結果集的表
partitions // 匹配的分區
type // 表示表的連接類型,
possible_keys // 表示查詢時,可能使用的索引
key // 表示實際使用的索引
key_len // 索引字段的長度
ref // 列與索引的比較
rows // 掃描出的行數(估算的行數)
filtered // 按表條件過濾的行百分比
Extra // 執行情況的描述和說明
我們把比較重要的參數提取出來進行詳細講解一下:

type列
表示連接類型,類型有ALL、index、range、 ref、eq_ref、const、system、NULL,這幾種類型從左到右,性能越來越高。一般一個好的sql語句至少要達到range級別。all級別應當杜絕

ALL:全表掃描,應當避免該類型
index:索引全局掃描,index與ALL區別爲index類型只遍歷索引樹
range:檢索索引一定範圍的行
ref:非唯一性索引掃描,返回匹配某個單獨值的所有行
eq_ref:唯一索引掃描,對於每個索引鍵,表中只有一條記錄與之匹配。常見主鍵或唯一索引掃描
const:表示通過一次索引就找到了結果,常出現於primary key或unique索引
system:system是const類型的特例,當查詢的表只有一行的情況下,使用system
NULL:MySQL在優化過程中分解語句,執行時甚至不用訪問表或索引,是最高的登記
key列
表示實際使用到的索引,如果爲NULL,則沒有使用索引

key_len列
表示使用索引長度

rows列
表示根據sql情況,預估表的掃描行數

extra列
表示詳細說明,注意該值包含十分重要的信息。一般該列存在下列值,常見的不太友好的值有:Using filesort, Using temporary

Using where // 表示不用讀取表中所有信息,僅通過索引就可以獲取所需數據,即使用列覆蓋索引
Using temporary // 表示需要使用臨時表來存儲結果集,常見於排序和分組查詢,如:group by ; order by
Using filesort // 表示無法利用索引完成的排序
Using join buffer // 表示使用了連接緩存,如果出現了這個值,建議根據查詢的具體情況可能需要添加索引來改進能。
Impossible where // 表示where語句會一直false,導致沒有符合條件的行(通過收集統計信息不可能存在結果)
Select tables optimized away // 這個值意味着sql優化到不能在優化了
No tables used // Query語句中使用from dual 或不含任何from子句
好了,我們對explain執行計劃做了一個基本的介紹,下面我們來看看in到底會不會走索引

構建測試條件
創建表如下:

CREATE TABLE `test` (
  `id` int NOT NULL AUTO_INCREMENT,
  `name` varchar(120) DEFAULT NULL COMMENT '姓名',
  `age` int DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='測試表';
插入數據

INSERT INTO `test`.`test`(`id`, `name`, `age`) VALUES (1, 'xiaoming', 18);
執行explain執行計劃

EXPLAIN SELECT * FROM test WHERE name  in ("lisi")
查看結果

 

可以看到in確實走了所以 idx_name,那是不是in永遠都會走索引呢?

我們通過存儲過程插入10000條數據

DELIMITER //
DROP PROCEDURE IF EXISTS insertTestData;
CREATE PROCEDURE insertTestData () BEGIN
    DECLARE i INT;    
    SET i = 0;
    WHILE i < 10000 DO
      INSERT INTO test(`name`, `age`) VALUES (CONCAT('xiaoming', CONCAT( i, '' )), 18);
        SET i = i + 1;
    END WHILE;
END //
 
CALL insertTestData();
DELIMITER ;
此時我們再看下是不是in繼續走索引

EXPLAIN SELECT * FROM test WHERE name  in ("lisi","xiaoming1")
發現依舊走索引

 

此時我們再插入2000條"lisi"這樣的數據

DELIMITER //
DROP PROCEDURE IF EXISTS insertTestData;
CREATE PROCEDURE insertTestData () BEGIN
    DECLARE i INT;    
    SET i = 0;
    WHILE i < 2000 DO
      INSERT INTO test(`name`, `age`) VALUES ('lisi', 18);
        SET i = i + 1;
    END WHILE;
END //
CALL insertTestData();
DELIMITER ;
執行依舊in走索引,那是不是意味着in一定走索引呢?

神奇的界限
當我們再繼續執行2次插入2000條"lisi",即數據庫有6000條name=“lisi”的數據時,神奇的發現in並不走索引了,如下圖

 
———————————————

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章