explain 是唯一可信的起点,必须在 sql 前加 explain 查看执行计划;重点关注 type(all/index 表示未有效走索引)和 key(null 表示未选索引),where 中对索引列使用函数或运算会导致索引失效。

EXPLAIN 是唯一可信的起点
别猜,直接在 SQL 前加 EXPLAIN。MySQL 优化器是否用索引,它说了算,不是你、不是文档、也不是经验。重点关注两个字段:type 和 key。type 是 ALL 或 index,基本等于没走有效索引;key 是 NULL,说明压根没选索引。其他字段如 rows(预估扫描行数)、possible_keys(可选但未被选的索引)也得扫一眼。
WHERE 条件里有没有函数或运算
对索引列用 YEAR()、DATE()、UPPER()、LEFT(),或者写成 age + 1 = 20,都会让索引失效。B+ 树存的是原始值,函数一包,就只能逐行计算比对。
- 错误写法:
WHERE DATE(created_at) = '2023-01-01' - 正确写法:
WHERE created_at >= '2023-01-01 00:00:00' AND created_at - MySQL 8.0+ 可建函数索引:
CREATE INDEX idx_created_date ON t ((DATE(created_at))),但别滥用
字段类型和查询值类型对得上吗
这是最隐蔽也最常踩的坑。比如 user_id 是 VARCHAR,你却写 WHERE user_id = 12345,MySQL 会把每行 user_id 都转成数字再比——相当于给索引列加了隐式 CAST,索引直接作废。
- 查
VARCHAR字段,值必须带单引号:WHERE user_id = '12345' - 查
INT字段,别传字符串:WHERE id = 123,而不是WHERE id = '123' - ORM 框架里也要核对:MyBatis 的
#{}和${}行为不同,JPA 实体字段类型和 SQL 参数类型要一致
联合索引的最左列是不是被跳过了
联合索引 (a, b, c) 不是“有 a 或 b 或 c 就能用”,而是必须从左边开始连续匹配。只查 b = 1 或 b = 1 AND c = 2,索引完全无效;a = 1 AND c = 2 也只能用上 a 这一列,c 不走索引。
- 范围查询(
>、、<code>BETWEEN)会截断后续列:WHERE a = 1 AND b > 10 AND c = 5中,c无法使用索引 - 想支持
WHERE b = ? AND c = ?,要么重建索引为(b, c, a),要么单独为b建索引 -
IS NULL在某些版本中不走联合索引最左列,需实测验证
EXPLAIN 看着 key 有值、type 是 ref,结果查询还是慢——这时候得看 rows 是否远超预期,以及是否触发了大量回表。索引用了,不等于高效。











