ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

MySQL 使用 IN 语句会走索引吗?别再凭印象写SQL了

MySQL 使用 IN 语句会走索引吗?别再凭印象写SQL了 前言很多开发同学存在两种极端认知传言IN不走索引一律改成EXISTS直觉IN和差不多肯定能正常命中索引。实际上两种说法都不准确。MySQL IN 能否走索引取决于版本、数据量、索引类型、优化器判断、子查询还是常量集合。本文区分两种最常见场景常量IN列表、IN(子查询)结合执行计划、案例、优化方案一次性讲清楚。环境说明MySQL 5.7 / 8.0InnoDB引擎一、场景1IN 后面是常量列表IN (1,2,3,4)SQL示例SELECT*FROMuserWHEREidIN(1001,1002,1003);✅结论正常可以走索引等价于多条OR条件优化器会识别为范围扫描range。EXPLAIN中 type 列显示range代表使用索引范围查找。关键限制IN 列表数值过多时优化器可能放弃索引选择全表扫描MySQL优化器会估算代价使用索引多次索引查找 回表全表扫描顺序读取磁盘。当IN内元素非常多优化器认为回表开销大于全表扫描直接切换ALL全表扫描。经验阈值没有固定数字由数据分布、页面缓存决定不要硬记“超过200条就不行”。索引失效常见坑-- 字段隐式转换索引失效SELECT*FROMuserWHEREphoneIN(13800138000,13900139000);-- phone 是 varcharIN 传入数字触发隐式转换索引无法使用规则索引字段参与运算、类型不匹配IN 同样无法使用索引。二、场景2IN 后面跟子查询IN (SELECT ...)这是最容易踩坑的地方也是网上谣言的来源。SELECT*FROMAWHEREA.idIN(SELECTB.idFROMBWHEREB.status1);MySQL 5.7 行为重点5.7优化器不会先执行子查询缓存结果会将IN(子查询)转化为EXISTS半连接semi-join。大部分情况下性能表现和EXISTS接近但存在特殊场景下优化器选择不佳出现低效执行计划。老版本MySQL 5.6及更早存在相关子查询嵌套循环问题性能很差这也是“IN不如EXISTS”说法的历史源头。MySQL 8.0 行为8.0对半连接、子查询优化大幅增强IN(子查询)、EXISTS、JOIN在很多场景下会生成完全相同的执行计划性能差距极小。重要误区澄清❌ 错误结论IN(子查询)不走索引✅ 事实能不能走索引不取决于语法是IN还是EXISTS取决于关联字段是否有索引、统计信息是否准确。三、IN、EXISTS、INNER JOIN 怎么选先回顾三种写法以业务SQL举例-- 写法1 IN(子查询)SELECTd.*FROMopenapi_interface_doc dWHEREverify_idf_idIN(SELECTverify_idf_idFROMopenapi_priceWHEREstatus1);-- 写法2 EXISTSSELECTd.*FROMopenapi_interface_doc dWHEREEXISTS(SELECT1FROMopenapi_price pWHEREp.verify_idf_idd.verify_idf_idANDp.status1);-- 写法3 JOINSELECTDISTINCTd.*FROMopenapi_interface_doc dINNERJOINopenapi_price pONd.verify_idf_idp.verify_idf_idWHEREp.status1;对比总结EXISTS驱动表为主表找到第一条匹配立即停止天然去重不需要DISTINCT适合主表数据量小、子查询匹配量大场景。IN(常量列表)简洁直观少量常量首选注意控制列表长度。INNER JOIN如果一对多会产生重复数据必须加DISTINCT额外消耗性能数据量大时尽量避免。现代MySQL 8.0三者性能差距大幅缩小优先看执行计划而不是凭经验选择语法。四、哪些情况IN一定不走索引IN 字段上存在函数运算-- 索引失效SELECT*FROMtableWHERECAST(str_idASUNSIGNED)IN(1,2);对索引列使用函数导致无法使用B树索引只能全表扫描。隐式类型转换字符串字段和数字互相比较索引失效。优化器代价评估后主动放弃索引IN列表值极多优化器认为全表扫描更快。没有建立对应索引无论IN/EXISTS缺少索引一切免谈。五、实用排查手段EXPLAIN判断是否走索引不要猜直接执行EXPLAINSELECTid,verify_idf_id,interfaceFROMopenapi_interface_docWHEREverify_idf_idIN(SELECTverify_idf_idFROMopenapi_priceWHEREstatus1);观察type字段range/ref正常使用索引ALL全表扫描同时留意key字段确认实际使用的索引名称。六、落地优化建议常量IN场景少量ID直接使用IN (?, ?, ?)如果IN元素上千条建议分批查询或者改用临时表关联。IN(子查询)场景MySQL5.7环境优先使用EXISTS稳定性更好MySQL8.0两种写法均可对比执行计划择优。禁止在索引字段上做转换、函数运算例如你业务中字符串ID需要数字排序不要在SQL内ORDER BY CAST(col AS UNSIGNED)会造成文件排序尽量上层业务内存排序。定期更新统计信息ANALYZETABLEopenapi_price,openapi_interface_doc;统计信息过时优化器容易做出错误选择明明可以走索引却选择全表扫描。七、总结IN(常量列表)通常可以走索引range列表过大有可能失效IN(子查询)MySQL5.7/8.0内部大多转化为半连接能否走索引由索引和数据分布决定不存在“天生不走索引”“IN不如EXISTS”是旧版本MySQL历史遗留经验不能直接套用到5.7、8.0语法只是表象索引、统计信息、执行计划才是性能核心遇到性能疑问永远先用EXPLAIN验证不要靠网传结论拍脑袋写SQL。
返回列表