当前位置: 首页 > news >正文

MySQL 索引失效的七种情况

我刚工作的时候,有次上线了个新功能,结果 SQL 查询慢得要命。DBA 帮我一看执行计划,发现索引失效了,全表扫描 2000 万条数据。

从那以后,我每次写完 SQL 都会检查索引是否失效,避免线上事故。

今天咱们就来聊聊 MySQL 索引失效的七种情况,看完这篇,你就能避开 90% 的索引失效坑。

索引失效是啥?

索引失效指的是:你以为 SQL 会走索引,结果 MySQL 优化器觉得走索引更慢,放弃了索引,改成全表扫描。

验证索引是否失效

-- 看执行计划EXPLAINSELECT*FROMusersWHEREage=25;

如果key字段是NULL,说明索引失效了。

情况 1:对索引字段用函数

问题:对索引字段用函数,索引失效。

-- age 有索引,但用了函数,索引失效EXPLAINSELECT*FROMusersWHERELEFT(name,5)='Alice';

为什么失效?索引存的是字段的原始值,不是函数计算后的值。MySQL 没法用索引快速定位,只能全表扫描。

优化方案:改成范围查询或者等号查询。

-- 优化后:用 LIKE 前缀匹配(能用索引)SELECT*FROMusersWHEREnameLIKE'Alice%';

如果一定要用函数,考虑生成列(Generated Column)+ 索引

-- 创建生成列,存 name 的前 5 个字符ALTERTABLEusersADDCOLUMNname_prefixCHAR(5)AS(LEFT(name,5))STORED;-- 给生成列加索引CREATEINDEXidx_name_prefixONusers(name_prefix);-- 查询时用生成列SELECT*FROMusersWHEREname_prefix='Alice';

情况 2:隐式类型转换

问题:查询条件的类型和索引字段的类型不一致,导致隐式类型转换,索引失效。

-- age 是 INT,但查询条件传了字符串,索引失效EXPLAINSELECT*FROMusersWHEREage='25';

为什么失效?MySQL 会把索引字段age转换成字符串,再和'25'比较(相当于对索引字段用了函数)。

优化方案:查询条件的类型和索引字段的类型保持一致。

-- 优化后:传 INTSELECT*FROMusersWHEREage=25;

隐藏得更深的类型转换

-- user_id 是 VARCHAR(50),但查询条件传了 INT,索引失效EXPLAINSELECT*FROMordersWHEREuser_id=123;

为什么失效?MySQL 会把user_id转换成数字,再和123比较(相当于对索引字段用了函数)。

优化方案:查询条件的类型和索引字段的类型保持一致。

-- 优化后:传字符串SELECT*FROMordersWHEREuser_id='123';

情况 3:模糊查询前缀通配符

问题LIKE查询用前缀通配符(%开头),索引失效。

-- name 有索引,但 LIKE 前缀通配符,索引失效EXPLAINSELECT*FROMusersWHEREnameLIKE'%Alice';

为什么失效?索引是有序的,LIKE '%Alice'没法用索引快速定位(因为'%'匹配任意字符)。

优化方案 1:改成后缀通配符(%结尾)。

-- 优化后:LIKE 后缀通配符(能用索引)SELECT*FROMusersWHEREnameLIKE'Alice%';

优化方案 2:用覆盖索引(只查索引字段)。

-- 优化后:覆盖索引(能用索引)SELECTnameFROMusersWHEREnameLIKE'%Alice';

优化方案 3:用全文索引(FULLTEXT INDEX)。

-- 创建全文索引ALTERTABLEusersADDFULLTEXTINDEXft_idx_name(name);-- 用 MATCH AGAINST 查询SELECT*FROMusersWHEREMATCH(name)AGAINST('Alice');

情况 4:OR 连接的条件中有字段没索引

问题OR连接的条件中,只要有一个字段没索引,整个索引失效。

-- age 有索引,但 name 没索引,索引失效EXPLAINSELECT*FROMusersWHEREage=25ORname='Alice';

为什么失效?MySQL 认为,既然name没索引,那还不如全表扫描(一次扫描搞定),不用先走索引再全表扫描。

优化方案 1:给OR连接的所有字段加索引。

-- 给 name 加索引CREATEINDEXidx_nameONusers(name);-- 再执行查询,索引生效SELECT*FROMusersWHEREage=25ORname='Alice';

优化方案 2:用UNION代替OR

-- 优化后:UNION(两个子查询都能走索引)SELECT*FROMusersWHEREage=25UNIONALLSELECT*FROMusersWHEREname='Alice';

情况 5:联合索引没遵循最左前缀

问题:联合索引有"最左前缀"原则,如果查询条件没用到最左前缀,索引失效。

-- 联合索引 (age, name)CREATEINDEXidx_age_nameONusers(age,name);-- 没用到最左前缀 age,索引失效EXPLAINSELECT*FROMusersWHEREname='Alice';

为什么失效?联合索引是先按age排序,再按name排序。如果没指定age,MySQL 没法快速定位。

优化方案:查询条件遵循最左前缀原则。

-- 能用索引的情况:WHEREage=25ANDname='Alice'-- 用到 (age, name)WHEREage=25-- 用到 (age)WHEREage>20ANDage<30-- 用到 (age)-- 不能用索引的情况:WHEREname='Alice'-- 没用到最左前缀 age

如果一定要查name,考虑单独给name建索引。

CREATEINDEXidx_nameONusers(name);

情况 6:索引选择性太差

问题:如果索引字段的选择性太差(比如性别字段,只有男/女两个值),MySQL 可能放弃索引,选择全表扫描。

-- gender 有索引,但选择性太差,MySQL 可能放弃索引EXPLAINSELECT*FROMusersWHEREgender='男';

为什么失效?如果gender='男'的记录占全表的 50%,那走索引还得回表 50% 的记录,不如全表扫描(减少随机 I/O)。

优化方案 1:如果业务允许,加其他条件缩小范围。

-- 优化后:加其他条件,缩小范围SELECT*FROMusersWHEREgender='男'ANDage=25;

优化方案 2:用覆盖索引(只查索引字段,不需要回表)。

-- 优化后:覆盖索引SELECTgenderFROMusersWHEREgender='男';

情况 7:优化器估计走索引更慢

问题:MySQL 优化器会根据统计信息估算走索引的成本,如果估计走索引更慢,会放弃索引。

-- 假设 users 表有 1000 万行,age=25 的有 900 万行-- MySQL 估计走索引要回表 900 万次,不如全表扫描EXPLAINSELECT*FROMusersWHEREage=25;

为什么失效?如果符合条件的记录太多,走索引要回表很多次(随机 I/O),全表扫描反而更快(顺序 I/O)。

优化方案 1:加LIMIT限制返回行数。

-- 优化后:加 LIMIT,MySQL 可能走索引SELECT*FROMusersWHEREage=25LIMIT10;

优化方案 2:用覆盖索引(只查索引字段,不需要回表)。

-- 优化后:覆盖索引SELECTageFROMusersWHEREage=25;

优化方案 3FORCE INDEX强制走索引(不推荐,除非你确定索引更快)。

-- 强制走 idx_age 索引SELECT*FROMusersFORCEINDEX(idx_age)WHEREage=25;

实战建议

1. 每次写完 SQL 都用 EXPLAIN 检查

这是最重要的建议。很多线上慢查询,都是因为索引失效,导致全表扫描。

2. 避免对索引字段用函数

如果一定要用函数,考虑生成列 + 索引。

3. 查询条件的类型和索引字段的类型保持一致

避免隐式类型转换,导致索引失效。

4. 模糊查询尽量用后缀通配符

如果一定要用前缀通配符,考虑全文索引。

5. OR 连接的条件要给所有字段加索引

或者改用UNION代替OR

6. 联合索引要遵循最左前缀原则

如果查询条件不遵循最左前缀,考虑单独给字段建索引。

7. 索引选择性太差时,考虑覆盖索引或者加其他条件缩小范围

如果优化器估计走索引更慢,会放弃索引。

总结

  • 索引失效的七种情况:
    1. 对索引字段用函数
    1. 隐式类型转换
    1. 模糊查询前缀通配符
    1. OR 连接的条件中有字段没索引
    1. 联合索引没遵循最左前缀
    1. 索引选择性太差
    1. 优化器估计走索引更慢
    • 实战建议:每次写完 SQL 都用 EXPLAIN 检查、避免对索引字段用函数、查询条件的类型和索引字段的类型保持一致、模糊查询尽量用后缀通配符、OR 连接的条件要给所有字段加索引、联合索引要遵循最左前缀原则、索引选择性太差时考虑覆盖索引
      如果你能把这七种情况讲清楚,并且能优化索引失效的 SQL,面试官绝对觉得你有实战经验。

实战代码都在我本地跑过,你可以放心复制。如果有问题,欢迎评论区交流!

http://www.jsqmd.com/news/875532/

相关文章:

  • 多重样本分割:提升异质性处理效应估计稳定性的关键技术
  • 【芯片测试】:6. 向量、Sequencer 指令与高速串行 IO
  • 工业物联网智能计量网络入侵检测:机器学习实战与边缘部署
  • LoRA专家混合技术评测:RAMoLE如何实现动态任务适配与性能提升
  • 机器学习赋能高维量子导引检测:从SVM到ANN的实践探索
  • C#/Halcon:简单介绍在AOI设备软件中的应用
  • 基于图元随机游走的网络嵌入:提升同质性与下游任务性能
  • 量子机器学习采样加速:热力学视角下的双向量子制冷器
  • 量子机器学习在消费电子异常检测中的应用与实战解析
  • Claude Code-入门篇-Claude-Code基础与环境配置
  • 为Claude Code配置Taotoken后端,告别封号与Token不足困扰
  • AI Agent安全治理框架缺失导致客户数据泄露?(Gartner 2024新评估模型首次落地解读)
  • 图数据管理与图机器学习:双向赋能的技术融合与实战解析
  • 含光热电站的冷、热、电综合能源系统优化调度【节点网络】附Matlab代码
  • 【芯片测试】:7. Action 与 Operating Sequence
  • 新手避坑指南:在Ubuntu 22.04上从零搭建Plexe-SUMO自动驾驶仿真环境
  • 年薪50万必备技能:.NET云原生架构实战,3分钟部署全球可用的微服务
  • GE 和 Runtime:不是上下游,是协同决策
  • Midjourney --style raw + 调色板协同失效?3步诊断流程+4类硬件级色彩配置冲突解决方案
  • 反应坐标映射:非马尔可夫开放量子系统的高效模拟方法
  • B物理反常的全局拟合:有效场论与机器学习解析新物理信号
  • 神经材质:NeRF之后,下一代数字内容的“皮肤”革命
  • Harness Engineering:麻绳还是马绳
  • SVM在频繁模式挖掘中的应用:从高维稀疏数据中提取判别性关联规则
  • Leslie矩阵建模:从种群动力学到捕食竞争与机器学习拟合
  • 从《原神》到《黑神话》都在用的AI Agent中间件:轻量级推理框架v0.9.3内部测试版首次泄露(仅限前500名开发者)
  • 别急着重启!深入理解Ubuntu 22.04的needrestart:守护进程、库文件与系统更新背后的原理
  • Telnet与SSH协议安全本质对比:从明文传输到公钥认证
  • 神经阴影:当AI学会“画影子”,实时渲染的下一个突破口
  • KNO标度律与粒子多重数:从QCD喷注结构到夸克-胶子鉴别的理论推导