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

为什么加了索引还慢?MySQL 索引失效 12 个排查点

这篇给你一个能直接拿去线上排查的版本,不讲玄学,只讲怎么定位。

先看结论

SQL 慢、明明建了索引,通常不是“索引没建”,而是:

  1. 建了但没用上
  2. 用上了但扫描行太多
  3. 用上了但回表成本太高

排查顺序就三步:EXPLAIN、看执行条件、改 SQL/索引设计。


一、先用 EXPLAIN 别猜

先跑:

EXPLAINSELECT*FROMordersWHEREuser_id=10086ANDstatus=1;

重点看这几个字段:

字段怎么看
type至少要到ref,最好range/const
key实际命中的索引
rows预估扫描行数,越小越好
Extra有没有Using filesortUsing temporary

如果key = NULL,先别急着骂 MySQL,通常是写法把索引“写废了”。


二、最常见的 12 个索引失效点

1) 在索引列上做函数

-- 失效写法SELECT*FROMuserWHEREDATE(create_time)='2026-03-29';

DATE(create_time)让 B+Tree 没法按原值匹配。

改成范围查询:

SELECT*FROMuserWHEREcreate_time>='2026-03-29 00:00:00'ANDcreate_time<'2026-03-30 00:00:00';

2) 隐式类型转换

-- phone 是 varchar,右边给了数字SELECT*FROMuserWHEREphone=13800138000;

字段类型和常量类型不一致,会导致转换,索引可能失效。
统一类型:

SELECT*FROMuserWHEREphone='13800138000';

3) 前导模糊匹配

SELECT*FROMproductWHEREnameLIKE'%手机';

%开头无法走普通索引。
可选方案:倒排索引(ES)、前缀搜索重构、全文索引。

4)OR两边索引条件不对称

SELECT*FROMordersWHEREuser_id=1ORremark='urgent';

一边可走索引,一边不走,优化器可能直接全表扫。
可拆成UNION ALL两段分别优化。

5) 复合索引没按最左前缀用

有索引(user_id, status, create_time),你却写:

SELECT*FROMordersWHEREstatus=1;

没带最左列user_id,这个复合索引通常用不上。

6) 范围条件后面的列无法继续高效利用

-- 索引:(user_id, create_time, status)SELECT*FROMordersWHEREuser_id=1ANDcreate_time>'2026-03-01'ANDstatus=1;

命中到范围列后,后续列的过滤收益会明显下降。
常见做法:调整复合索引顺序,把等值高选择度列尽量放前面。

7)!=/<>/NOT IN选择性差

SELECT*FROMuserWHEREstatus<>1;

这类条件经常要扫大范围,优化器可能放弃索引。

8) 回表太重,优化器宁可全表扫

SELECT*FROMordersWHEREuser_id=10086;

如果命中行太多,SELECT *触发大量回表,可能不划算。
尽量改成覆盖索引需要的列:

SELECTid,user_id,statusFROMordersWHEREuser_id=10086;

9) 排序和索引方向不匹配

SELECT*FROMordersWHEREuser_id=1ORDERBYcreate_timeDESC;

索引不匹配时会出现Using filesort
要么补合适索引,要么减少排序数据集。

10) 分组导致临时表

GROUP BY字段若没有合适索引,常见Using temporary
先过滤再分组,或增加对应索引。

11) 统计信息过旧

数据分布变化后,优化器可能选错执行计划。
可执行:

ANALYZETABLEorders;

12) 小表全扫本来就更快

这个不是失效,是优化器做了正确决策。
几千行的小表,全表扫可能比走索引+回表还快。


三、一个实战排查流程(我平时这么走)

  1. 抓慢 SQL(慢日志 / APM)
  2. EXPLAINkey/type/rows/Extra
  3. 检查是否存在上面 12 类问题
  4. 优先改 SQL 写法(函数、类型、范围、SELECT *
  5. 再改索引设计(复合索引顺序、覆盖索引)
  6. 回归压测,对比耗时和扫描行

先改 SQL 往往比“无脑加索引”收益更大。


四、给你一套可复用的优化模板

慢 SQL:

SELECT*FROMordersWHEREDATE(create_time)='2026-03-29'ANDstatus=1ORDERBYcreate_timeDESCLIMIT20;

优化后:

-- 1) 改写时间条件,避免函数-- 2) 减少返回列,尽量覆盖索引SELECTid,user_id,status,create_timeFROMordersWHEREcreate_time>='2026-03-29 00:00:00'ANDcreate_time<'2026-03-30 00:00:00'ANDstatus=1ORDERBYcreate_timeDESCLIMIT20;

配套索引思路:

-- 按你的查询条件设计复合索引(示例)CREATEINDEXidx_orders_status_ctimeONorders(status,create_time);

五、最后的建议

排查索引问题时,别问“有没有索引”,要问三件事:

  1. 这个索引有没有被用到
  2. 用到后扫描行是不是还很大
  3. 回表和排序是不是把收益吃掉了

你把这三件事盯住,MySQL 慢查询会少掉一大半。

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

相关文章:

  • 文件驱动的智能体通信:构建高可靠分布式协作系统的架构解析与实践指南
  • 如何用TensorFlow的DeepLabV3+实现Cityscapes街景分割?完整训练+验证+可视化流程
  • FastAPI热重载卡顿?降级uvicorn到0.20.0可能是最快解决方案(附原因分析)
  • Nacos 2.4.1 连接人大金仓踩坑记:除了改驱动,这个函数也得动!
  • IS31FL3733A LED驱动库深度解析与嵌入式实战指南
  • Vivado Chipscope调试实战:如何快速定位FPGA设计中的DRC警告(附避坑指南)
  • 量子启发算法在高维推理任务中的应用研究
  • 保姆级教程:在MMDetection3D中手把手调试PointPillars网络结构(附代码逐行解析)
  • Pololu Maestro伺服控制器底层通信协议与嵌入式驱动开发
  • GyverMotor2电机库:嵌入式直流电机控制工程实践指南
  • jpegenc-pio:MCU零依赖JPEG编码器深度解析
  • LSM303DLHC六轴IMU硬件设计与磁场校准实战指南
  • 手把手教你排查Qt链接错误:从‘Qt5Core.lib缺失‘到完美运行的调试实录
  • `git rebase` 和 `git merge` 的区别是什么?
  • Video2X终极教程:用AI免费无损放大视频到4K的简单方法
  • 从零配置Realsense D435的ROS2工作空间:不只是安装SDK,还有Gazebo仿真与真实设备切换
  • Comsol仿真代做:带你开启多物理场模拟之旅
  • 安卓开发者必看:解决Google Play服务报错的5种实战方法(附详细步骤)
  • 专业机器人夹爪厂商盘点,适配机器人末端抓取全场景 - 品牌2026
  • ESP32+LVGL实战:手把手教你搞定ST7789屏幕镜像显示(附完整代码)
  • 新手必看:用T16IZ遥控器给PX4无人机对频,保姆级图文教程(附接线避坑点)
  • 虚拟机固定IP配置实战:从DHCP到静态设置的完整指南
  • SpikingJelly框架实战:5步搞定脉冲神经网络MNIST分类(附PyTorch代码)
  • TVout库:AVR单片机纯软件复合视频输出方案
  • Windows下OpenClaw安装指南:一键连接GLM-4.7-Flash模型
  • 嵌入式C语言实现面向对象编程三大特性
  • Spring AI MCP实战避坑指南:从部署到调试的常见问题解析
  • 基于Transformer的多变量时序预测:Matlab实战指南
  • 从Calculator到Python Calculator:Paraview数据处理进阶全攻略(含NumPy函数表)
  • 串口通信优化:FIFO与协议帧技术实践