MySQL索引失效的典型原因包括:1.隐式类型转换;2.索引列上使用函数或表达式;3.违反最左前缀原则;4.OR条件中含非索引列;5.索引选择性过低;6.范围查询后缀列失效;7.前导模糊查询;8.对索引列进行运算;9.使用!=或IS NULL等操作符;10.数据占比过大导致优化器放弃索引。

TP5.1 项目上线后数据库 CPU 突然飙升到 95% 以上,页面响应超 10 秒,日志里反复出现同一张表的 SELECT 查询,但 WHERE 条件字段明明建了单列索引——问题往往出在多条件联合查询时索引失效,必须用 EXPLAIN 看清真实执行路径,再针对性加复合索引。
定位高负载 SQL
进入 MySQL 命令行,执行 【SET GLOBAL slow_query_log = ON;】 开启慢日志(若未开启),同时确认 long_query_time 设为 1(秒):SET long_query_time = 1;
查出最近 5 条最耗时的慢 SQL:SELECT query_time, sql_text FROM mysql.slow_log ORDER BY query_time DESC LIMIT 5;
复制其中一条典型语句,例如:SELECT id,name,created_at FROM user_order WHERE status = 1 AND is_pay = 1 AND created_at > '2023-01-01' ORDER BY created_at DESC;
用 EXPLAIN 看执行计划
在该 SQL 前加上 EXPLAIN 关键字并执行:EXPLAIN SELECT id,name,created_at FROM user_order WHERE status = 1 AND is_pay = 1 AND created_at > '2023-01-01' ORDER BY created_at DESC;
重点看 type 字段——如果显示 ALL 或 index,说明走了全表扫描或全索引扫描;再看 key 字段是否为 NULL,NULL 就代表没走任何索引;rows 值若接近表总行数(比如表有 80 万行,rows 显示 792341),基本可断定索引失效。
注意:EXPLAIN 结果中的 Extra 出现 Using filesort 或 Using temporary,说明排序或分组无法利用索引,会极大拖慢速度。
设计复合索引
第一步:提取 WHERE 条件中所有等值查询字段,按出现顺序排列——status 和 is_pay 都是 = 查询,优先放前面;created_at 是范围查询(>),必须放在等值字段之后,否则后续字段无法命中索引。
第二步:把 ORDER BY 字段追加到索引末尾——本例是 ORDER BY created_at DESC,而 created_at 已在 WHERE 中作为范围条件,直接复用即可,无需重复添加;但如果排序字段与 WHERE 中不一致(比如 ORDER BY id),就得额外补上。
第三步:生成建索引语句:ALTER TABLE user_order ADD INDEX idx_status_ispay_ctime (status, is_pay, created_at);
这一步不能颠倒字段顺序。如果写成 (created_at, status, is_pay),MySQL 在遇到 created_at > '2023-01-01' 时,后面两个等值字段就完全失效了——【复合索引最左前缀原则不可绕过】。
验证索引生效
方法一:再次执行 EXPLAIN,确认 key 列显示为 idx_status_ispay_ctime,type 变为 ref 或 range,rows 值从几十万降到几百或几千。
方法二:用 FORCE INDEX 强制走新索引对比性能:SELECT id,name,created_at FROM user_order FORCE INDEX(idx_status_ispay_ctime) WHERE status = 1 AND is_pay = 1 AND created_at > '2023-01-01' ORDER BY created_at DESC;
方法三:观察线上监控——top -H 查看 mysqld 进程线程 CPU 占用,5 分钟内应从 95%+ 降至 30% 以下;同时用 SHOW PROCESSLIST 观察该 SQL 的 Time 值是否从数百秒降至 0.x 秒。


















