mysql索引失效指已建索引未被使用,核心原因在于sql写法、参数类型、执行计划与索引结构不匹配;需通过explain查type=all、key=null等指标快速定位,并重点规避函数操作、隐式类型转换、左模糊查询、or滥用及最左前缀违规五类java常见场景。

Java 项目里 MySQL 索引失效,不是“有没有建索引”的问题,而是“建了但没用上”。核心在于 SQL 写法、参数传值、数据类型和执行计划是否匹配索引结构。排查要快准狠,优化要直击根源。
先看执行计划:EXPLAIN 是第一道门槛
在 Java 中执行 SQL 前,务必把语句复制到 MySQL 客户端跑一遍 EXPLAIN:
-
type 字段:出现
ALL(全表扫描)或index(全索引扫描),基本确认索引未生效 -
key 字段:为
NULL表示完全没走索引;显示索引名才说明被选中 - rows 字段:预估扫描行数远大于实际结果集(比如查 1 条却扫 10 万行),说明索引过滤效果差
-
Extra 字段:含
Using filesort或Using temporary,说明排序/分组没走索引,可能因 ORDER BY 或 GROUP BY 列不在索引覆盖范围内
重点盯这五类 Java 场景
很多失效不是 SQL 本身错,而是 Java 层传参或拼接埋的雷:
-
函数包裹索引列:比如
WHERE DATE(create_time) = ?—— 即使 create_time 有索引也废掉。改成范围写法:create_time >= ? AND create_time ,Java 传入当天起止时间 -
隐式类型转换:数据库字段是
VARCHAR(如 phone),Java 却传Long或int参数(phone = 13800138000)。MySQL 自动转字符串导致索引失效。解决:MyBatis 显式指定#{phone, jdbcType=VARCHAR},或 Java 层统一传 String -
LIKE 左模糊:
name LIKE '%关键词'必然全表扫。能改前缀就改(LIKE '关键词%');否则考虑全文索引,或应用层加反向索引表 -
OR 连接非索引列:如
WHERE status = ? OR remark LIKE '%xxx%',remark 无索引,整个条件放弃索引。拆成两个查询 + UNION,或给 OR 两边都建合适索引 -
联合索引顺序错配:建了
INDEX idx_user_dept (user_id, dept_id),但 Java 查询只写WHERE dept_id = ?—— 最左前缀不满足,索引直接跳过。确保 WHERE 条件从索引最左列开始,且中间不断层
检查索引本身是否“健康”
别默认索引一定有效:
- 用
SHOW INDEX FROM table_name确认索引存在,且字段顺序、类型与查询一致 - 查选择性:
SELECT COUNT(DISTINCT column)/COUNT(*) AS selectivity FROM table。若小于 0.05(比如性别字段),索引价值极低,优化器大概率弃用 - 注意 NULL 值影响:普通索引不存储全 NULL 行,
WHERE col IS NULL可能走索引,但IS NOT NULL很难走 —— 若高频查询,可考虑加NOT NULL约束或单独建索引 - 小表不走索引很正常:几万行以下的表,全表扫描可能比索引回表更快,这是优化器合理决策,不算“失效”
Java 层能做的加固动作
光改 SQL 不够,代码层也要配合:
- MyBatis 动态 SQL 避免拼接函数:
<if test="date != null">AND create_time >= #{dateStart} AND create_time </if>,别写DATE(create_time) = #{date} - Spring Data JPA 查询方法命名严格遵循字段名,避免自定义 JPQL 时手误加函数
- 开启 MySQL 慢日志(
slow_query_log = ON),结合long_query_time = 1,快速捕获线上失效 SQL - 用
OPTIMIZER_TRACE深挖优化器为何放弃索引(需临时开启):SET optimizer_trace="enabled=on"; SELECT ...; SELECT * FROM information_schema.OPTIMIZER_TRACE;
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











