mybatis 生成的 sql 索引失效主因是隐式类型转换、函数包裹、like前缀通配及联合索引不匹配等导致mysql优化器弃用索引,需通过explain结合真实参数定位瓶颈。

MyBatis 生成的 SQL 为什么 EXPLAIN 显示没走索引
不是 MyBatis 本身导致索引失效,而是它默认拼接的 SQL 很容易触发隐式类型转换、函数包裹或参数绑定方式不当,让 MySQL 优化器主动放弃索引。
常见现象:EXPLAIN 的 type 是 ALL 或 index,key 列为 NULL,哪怕字段明明建了索引。
- 字符串字段用
#{}传数字(如user_id = #{id},但数据库里user_id是VARCHAR,而 Java 传的是Integer)→ 触发隐式转换,索引失效 - WHERE 条件里对字段用了函数,比如
WHERE DATE(create_time) = '2024-01-01'→ 索引无法下推 -
LIKE查询以%开头:name LIKE '%张%'→ 全表扫描 - 联合索引字段顺序和查询条件不匹配,比如索引是
(a, b, c),但只查WHERE b = ?→ 跳过前导列,索引失效
怎么用 EXPLAIN 快速定位 MyBatis 慢 SQL 的执行瓶颈
别直接在 MyBatis XML 里猜,先拿到真实执行的 SQL,再人工 EXPLAIN —— 因为 MyBatis 日志输出的 SQL 可能含占位符,需替换后验证。
- 开启 MyBatis 日志(如 Log4j 配置
logging.level.org.apache.ibatis=DEBUG),从日志里复制完整 SQL,注意把?替换成实际参数值(字符串加单引号,数字不加) - 在 MySQL 客户端执行
EXPLAIN FORMAT=TREE SELECT ...(MySQL 8.0+ 推荐),比传统格式更直观看到访问路径和过滤比例 - 重点盯三列:
type(访问类型,ref/range合理,ALL危险)、rows(预估扫描行数,远大于结果集就可疑)、filtered(过滤率,低于 10% 说明条件选择性差) - 如果
key显示用了索引,但rows极大,可能是索引区分度低(如性别字段建了索引),或者统计信息过期,可执行ANALYZE TABLE table_name
#{} 和 ${} 在索引场景下的致命区别
#{} 是预编译参数,安全但可能因类型不匹配导致隐式转换;${} 是字符串拼接,危险但能完全控制 SQL 结构——某些动态索引场景下,你不得不选后者,但必须严格校验输入。
- 用
#{}时,确保 Java 参数类型和数据库字段类型一致:MySQL 的TINYINT(1)布尔字段,Java 用Boolean而非Integer;VARCHAR字段对应String,别传Long - 需要动态表名/列名(如分表查询),只能用
${},但必须配合白名单校验:if (!ALLOWED_TABLES.contains(tableName)) throw new IllegalArgumentException(); - 避免
${id}拼在 WHERE 条件里替代#{id}——这既不安全,也不解决索引问题,只是把风险转嫁给应用层
联合索引设计与 MyBatis 查询条件的对齐要点
MyBatis 的 <if></if> 标签让 WHERE 条件变成“可选组合”,这恰恰最容易破坏联合索引的最左匹配原则。
- 按查询频率 + 区分度排序建联合索引:高频且高区分度的字段放最左,比如
(status, create_time, user_id)比(user_id, status, create_time)更适合WHERE status = ?主查询 - 如果 MyBatis 中有多个
<if test="xxx != null"></if>,优先保证前导列必填,或拆成不同 SQL(用<choose></choose>分支),避免一个 SQL 试图覆盖所有字段组合 - 覆盖索引能减少回表:把 SELECT 的字段也纳入联合索引末尾,例如查询
SELECT id, name FROM user WHERE dept_id = ? AND status = ?,索引设为(dept_id, status, name, id) - 注意
ORDER BY字段:如果 MyBatis SQL 含ORDER BY create_time DESC,而索引是(dept_id, create_time),则必须确保dept_id条件是等值查询,否则无法利用索引排序
真正卡住人的往往不是不会建索引,而是没意识到 MyBatis 的动态 SQL 会把“理论上能走索引”的语句,变成优化器眼中的“不可预测路径”。每次改完 XML,务必拿真实参数跑一遍 EXPLAIN。










