首页 游戏 软件 资讯 排行榜 专题
首页
AI教程
SQL索引失效六大场景详解与排查优化指南

SQL索引失效六大场景详解与排查优化指南

热心网友
34
转载
2026-05-28

前两天收到一位读者的私信,语气挺着急的,说面试时被问懵了:“你写的SQL凭什么不走索引?”

面试官:你写的 SQL 凭什么不走索引?我给他复现了 6 种翻车现场

他把SQL发过来一看,问题很典型:

SELECT * FROM user WHERE DATE(create_time) = '2026-05-20';

表里的create_time字段明明建了索引,查询却慢如蜗牛。原因就在于那个DATE()函数——不是索引没用,而是写法让MySQL根本用不了索引。

这类问题在开发和面试中太常见了。“索引失效”的结论谁都能背,但一到自己写代码,该踩的坑一个不少。今天就把最常见的六种“翻车现场”摆出来,每种都附上可复现的SQL,跑一遍就全明白了。

翻车①:在索引列上用了函数

-- 失效 ❌
SELECT * FROM user WHERE DATE(create_time) = '2026-05-20';

-- 有效 ✅
SELECT * FROM user WHERE create_time >= '2026-05-20 00:00:00'
AND create_time < '2026-05-21 00:00:00';

为什么第一种写法会失效?很简单,B+树索引里存储的是字段的原始值,而不是经过DATE()函数处理后的结果。当你对索引列进行函数运算时,MySQL就没办法利用索引的有序性进行快速定位了,只能老老实实地把每一行数据都拿出来计算一遍,结果就是全表扫描。

当然,MySQL 8.0.13之后引入了函数索引,可以通过创建虚拟列索引来绕过这个问题。但说实话,对于这种日期范围查询,直接修改查询条件,写成第二种范围查询的形式,才是更通用、更优雅的解决方案——不挑数据库版本,也不占用额外的存储空间。

类似的坑还有不少:YEAR(create_time)MONTH(create_time)LENGTH(name)ABS(amount)……记住一个原则:只要索引列出现在了函数或表达式的参数位置,这个索引基本上就宣告失效了。

翻车②:隐式类型转换

-- 假设 phone 字段是 VARCHAR 类型,建有索引
-- 失效 ❌
SELECT * FROM user WHERE phone = 13800138000;
-- 有效 ✅
SELECT * FROM user WHERE phone = '13800138000';

这个坑非常隐蔽。当MySQL遇到字符串类型的索引列与数字进行比较时,它会尝试将字符串转换为数字。这个转换动作一旦发生在索引列上,就等同于对列进行了函数操作,索引自然就失效了。

最麻烦的是,这种查询语法完全正确,能正常返回结果,你很可能根本意识不到底层已经悄悄变成了全表扫描。排查时可以用EXPLAIN命令看看Extra列,如果出现Using where; Using index可能还在用索引,但如果type直接显示为ALL

还有一个更隐蔽的“兄弟”:JOIN时字符集不一致。

-- 表 A(utf8mb4) JOIN 表 B(latin1)
-- 关联字段都有索引,但 JOIN 就是慢
SELECT * FROM a INNER JOIN b ON a.user_id = b.user_id;

当两张表的字符集不同时,MySQL会自动将低优先级的字符集(如latin1)转换为高优先级的字符集(如utf8mb4)。如果这个转换恰好发生在被驱动表的索引列上,那么该索引就会失效,导致全表扫描。

更坑的是,这种情况用EXPLAIN可能都看不出来异常,rows值显示正常,Extra列也没有警告。需要使用SHOW WARNINGS命令才能看到隐式转换的提示信息。

判断方法很简单:

SHOW CREATE TABLE a; -- 查看 CHARSET 和 COLLATE
SHOW CREATE TABLE b; -- 查看 CHARSET 和 COLLATE,不一致就要小心了

解决方案有两个:

  1. 统一字符集:这是根治之法,强烈推荐。
  2. 临时方案:如果无法立即修改表结构,可以尝试将转换函数写在非索引列的一侧。例如,假设a表是utf8mb4,b表是latin1且b.user_id有索引,可以这样写:
-- 正确写法:函数写在非索引列上,索引列保持干净 ✅
SELECT * FROM a INNER JOIN b ON CONVERT(a.user_id USING latin1) = b.user_id;

-- 错误写法:函数写在索引列上 = 索引报废 ❌
SELECT * FROM a INNER JOIN b ON a.user_id = CONVERT(b.user_id USING utf8mb4);

通过CONVERT(a.user_id USING latin1)将a表的值转为latin1再去匹配b表,可以保住b.user_id的索引。但要注意,latin1字符集不支持emoji等4字节字符,转换可能导致数据截断,这只应作为临时过渡方案。

翻车③:OR 条件

-- 假设 id 有索引,name 也有索引
-- 可能失效 ❌
SELECT * FROM user WHERE id = 1 OR name = '张三';

-- 有效 ✅
SELECT * FROM user WHERE id = 1
UNION
SELECT * FROM user WHERE name = '张三';

OR条件的语义是“满足任意一个条件即可”,这意味着MySQL需要分别获取两个条件的结果集再进行合并。问题在于,如果OR两边的字段并非都有索引,或者优化器经过成本估算后,认为分别走索引再合并的成本比直接全表扫描还要高,它就会选择放弃使用索引。

MySQL 5.0之后引入了Index Merge优化,在某些特定情况下,OR条件也能利用索引(执行计划中type会显示为index_merge)。但这个优化很“玄学”,严重依赖于具体的数据分布和优化器的成本估算,并不稳定可靠。

翻车④:LIKE 通配符在开头

-- 失效 ❌
SELECT * FROM user WHERE name LIKE '%张三%';
-- 有效 ✅
SELECT * FROM user WHERE name LIKE '张三%';

这背后的原理与B+树索引的有序性有关。'张三%'这种前缀匹配的查询,可以快速定位到索引中“张”字开头的位置,然后向后扫描。而'%张三%'这种前后都模糊的查询,由于无法确定前缀,优化器不知道从索引的哪个位置开始查找,只能退而求其次选择全表扫描。

MySQL 5.6引入的ICP(Index Condition Pushdown,索引条件下推)优化能稍微缓解这个问题:存储引擎层可以先用索引过滤掉一部分明显不匹配%张三%的行,减少需要回表检查的行数。此时EXPLAINExtra列会显示Using index condition

但需要警惕:Using index condition不等于覆盖索引。Using index才代表覆盖索引,即所需数据可以直接从索引中获取,无需回表。对于SELECT *这类查询,不可能覆盖所有列。ICP只是一个“减速带”,并非“刹车”——查询的type很可能依然是ALL,该慢的还是慢。

翻车⑤:联合索引没用最左前缀

-- 联合索引:(city, age, name)
-- 有效 ✅
SELECT * FROM user WHERE city = '北京' AND age = 25;
-- 有效 ✅
SELECT * FROM user WHERE city = '北京' AND name = '张三'; -- city 走了索引,name 走不了
-- 失效 ❌
SELECT * FROM user WHERE age = 25 AND name = '张三'; -- 跳过了 city

可以把联合索引想象成一本电话黄页:先按城市排序,同一个城市里再按年龄排序,同一年龄里再按姓名排序。如果你跳过最左边的“城市”直接查“年龄”和“姓名”,就相当于让人在没有城市目录的整本黄页里盲目翻找,效率极低。

这就是“最左前缀原则”:查询条件必须从联合索引定义的最左侧列开始,不能跳过中间的列。

这里有个常见的误解:写成WHERE age = 25 AND city = '北京',条件顺序反了会不会失效?答案是:不会。MySQL的查询优化器很聪明,它会自动重排WHERE条件的顺序,只要你的查询条件里包含了最左列city,索引就依然有效。

翻车⑥:NOT IN / != / <>

-- 失效 ❌
SELECT * FROM user WHERE status != 1;
SELECT * FROM user WHERE status <> 1;
SELECT * FROM user WHERE status NOT IN (1, 2);

-- 某些情况下有效(看数据分布)✅
SELECT * FROM user WHERE status IN (2, 3, 4);

索引的设计初衷是快速定位少量数据。而NOT IN!=操作的语义是“排除少数,选择剩下的大部分”。对于优化器来说,如果“大部分”数据都需要被选中,那么走索引反而可能更慢——因为通过索引查找需要大量的随机IO回表操作,其成本可能比直接顺序扫描整个表的成本还要高。

当然,在极端情况下,比如status = 1的记录占了95%,那么查询status != 1(实际只取5%的数据)是有可能走索引的。但更稳妥的做法是,如果业务允许,尽量将否定查询改写为肯定的范围查询,例如用status IN (2,3,4,...)来代替status NOT IN (1)。不过要注意,这种方法高度依赖于数据分布,一旦数据比例发生变化,执行计划就可能改变,因此并不推荐作为常规方案。

?️ 排查工具箱:怎么证明索引失效了?

了解了上面六种场景,回到实际问题:怎么快速判断自己的SQL有没有踩坑?记住下面三个EXPLAIN的排查动作,即插即用:

1. EXPLAIN 看 type 列:ALL → 基本就是全表扫描
2. EXPLAIN 看 key 列:NULL → 索引完全没被用上
3. EXPLAIN FORMAT=JSON 看 used_key_parts 数组:为空 → 索引有名字但没真正用

面试一句话总结

面试时被问到“索引失效的场景有哪些”,其实不需要死记硬背上面六条。抓住一个核心逻辑就够了:索引失效,本质上是查询条件破坏了B+树索引的“最左匹配”和“有序定位”能力。

拿这个逻辑去套:

  • 函数/类型转换:列的值被改了,B+树里找不到改完后的值。
  • OR条件:需要合并多个不连续的范围,优化器觉得不如全扫。
  • %like:不知道前缀是什么,无法在索引中定位起点。
  • 跳过最左列:不知道从索引的哪一段开始翻。
  • NOT IN:要取大部分数据,顺序扫描可能比回表更划算。

想通了“能不能从B+树左侧开始快速定位”这个根本,索引失效的问题就不再需要死记硬背,而是可以推理出来了。

来源:https://developer.aliyun.com/article/1737494
免责声明: 游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。

相关攻略

营地生存新手必读手册:高频问题全解
游戏攻略
营地生存新手必读手册:高频问题全解

初入《求生之城》的指挥官们,是否对营地内的诸多玩法感到困惑?这份详尽的新手FAQ将为你解答开荒期最常见的问题,助你快速上手,规避常见误区,更顺畅地沉浸于这场生存冒险。 1 对局内技能卡下方的“太阳花”图标代表什么? 此图标是英雄的“升魂”路线标识。在单局战斗中,每位英雄可能拥有多条不同的成长路径,

热心网友
05.28
四种无向量检索增强生成方案对比:BM25、GraphRAG、树搜索与智能体
AI教程
四种无向量检索增强生成方案对比:BM25、GraphRAG、树搜索与智能体

当我们谈论RAG(检索增强生成)时,向量检索几乎是默认的起点。它通过语义相似度来寻找相关文档,这听起来很智能,但问题恰恰出在这里:它优化的是“语义相似度”,而不是逻辑准确性。 举个例子,“不允许退货的政策”和“允许退货的政策”这两个查询,在向量空间里可能产生几乎相同的嵌入(embedding)。模型

热心网友
05.28
龙渊王座6月4日首测开启 史诗征程静待王者加冕
游戏攻略
龙渊王座6月4日首测开启 史诗征程静待王者加冕

备受瞩目的西方魔幻硬核MMO手游《龙渊王座》今日正式官宣,其首次封闭测试时间确定为6月4日。游戏构建了宏大的史诗世界观,提供四大经典职业选择,并拥有深度的角色养成系统与热血激情的PVP竞技玩法。一场关乎荣耀与征服的冒险史诗,即将正式启程。 测试信息 测试类型:限量封闭、计费、删档测试。 测试时间:2

热心网友
05.28
Linux系统安装Apache Tomcat 7.0.42详细教程
AI教程
Linux系统安装Apache Tomcat 7.0.42详细教程

在Linux系统上搭建稳定可靠的Tomcat 7运行环境,是众多Java Web项目部署的基石。尽管整体流程并不复杂,但版本选择与配置细节往往决定了部署的成败。本文将提供一份从零开始的详尽指南,帮助您高效完成安装,并有效规避常见陷阱。 一、安装与配置 JDK 环境 Tomcat 7作为Java Se

热心网友
05.28
猫与梦王国版本更新内容详解
游戏攻略
猫与梦王国版本更新内容详解

爱的真谛究竟是什么?是守护、陪伴,还是共同编织的梦想? 通往童话世界的入口已再次开启,在这柔软而漫长的绮丽梦境中,那位可爱的袖珍猫娘,依然在静谧的角落,等待着您的归来与邂逅。

热心网友
05.28

最新APP

宝宝过生日
宝宝过生日
应用辅助 04-07
台球世界
台球世界
体育竞技 04-07
解绳子
解绳子
休闲益智 04-07
骑兵冲突
骑兵冲突
棋牌策略 04-07
三国真龙传
三国真龙传
角色扮演 04-07

热门推荐

Notion AI内容改写与语气风格调整操作详解
AI资讯
Notion AI内容改写与语气风格调整操作详解

NotionAI能直接修改文本语气和风格。选中文字后右键使用“AskAI”功能,输入具体指令即可生成并替换新文本。也可用斜杠命令控制风格参数,指令需具体明确。处理批量邮件时可结合数据库与AI属性,自动填充变量并统一语气。通过隐藏指令块提供上下文,能更精准地控制输出风格。操作前建议备份原文。

热心网友
05.28
免费AI PPT制作教程 提升办公文档效率与质量
AI教程
免费AI PPT制作教程 提升办公文档效率与质量

如何利用免费AI PPT生成工具,轻松提升办公文档质量与效率 在当今快节奏的职场环境中,制作一份专业、高效且视觉出众的演示文稿,常常是一项极具挑战性的任务。值得庆幸的是,随着人工智能技术的飞速发展与普及,一系列智能办公工具应运而生,正在彻底改变传统文档制作模式。本文将深入探讨,如何借助WPS AI这

热心网友
05.28
高速追尾男子站护栏挥手警示 后方车辆注意避让
业界动态
高速追尾男子站护栏挥手警示 后方车辆注意避让

高速公路上车流密集、车速快,一旦发生交通事故,后续处置的每一个环节都直接关系到生命安全。近日,在沪渝高速湖北仙桃段,发生了一起令人警醒的追尾事故,而当事司机随后的“危险操作”,更是让赶到现场的交警惊出一身冷汗。 4月6日,在沪渝高速仙桃段,驾驶人代某驾驶一辆白色轿车在快车道行驶。当时前方车流量大,车

热心网友
05.28
OpenSpec入门指南:从零开始掌握规范编写
AI资讯
OpenSpec入门指南:从零开始掌握规范编写

OpenSpec是一款规范驱动开发的开源工具,旨在解决AI编程中因需求模糊导致的代码偏差问题。它通过结构化变更文件夹管理提案、任务与规范,确保开发前达成技术共识。其工作流程包括起草提案、审查对齐、实施任务和存档更新,支持从初始化到归档的完整变更周期,提升人机协作的精确性与可控性。

热心网友
05.28
如何用Kimi快速提取长篇访谈录音转写稿核心要点
AI资讯
如何用Kimi快速提取长篇访谈录音转写稿核心要点

手头有一份长达数万字的访谈录音转写稿,密密麻麻的文字读起来,很难迅速定位关键信息。别担心,借助Kimi就能从中提炼出核心要点。这里整理了五种实用操作路径,可根据需求灵活选用。 首先准备好转写稿,推荐使用TXT、DOCX或PDF格式。接着,根据具体场景选择一种方法即可。 一、角色驱动式指令解析 这种方

热心网友
05.28