MySQL 索引失效的常见写法
发布时间:2026-07-19 15:37       
更新时间:2026-07-20 05:34
索引明明建了,查询还是全表扫描,多数时候不是索引的问题,是 SQL 写法让优化器用不上它。四类写法占了失效场景的大头,逐个对照检查。
## 一、索引列被函数包裹
```
-- 失效:对列套了函数
SELECT * FROM orders WHERE DATE(created_at) = '2026-10-01';
-- 有效:把运算挪到常量一边
SELECT * FROM orders
WHERE created_at >= '2026-10-01' AND created_at < '2026-10-02';
```
索引列一旦参与函数运算,B+ 树(MySQL 索引的底层数据结构)就没法按原值定位,只能整表逐行算。改写思路就一条:列保持原样,函数和计算放到等号另一边。EXPLAIN 的 Extra 列出现全表扫描加 Using where 的组合,基本可以确认是这类写法。
## 二、隐式类型转换
phone 字段是 VARCHAR,条件却写成数字:
```
SELECT * FROM users WHERE phone = 13800000000; -- 失效
SELECT * FROM users WHERE phone = '13800000000'; -- 正确
```
数字与字符串比较时,MySQL 会把列的每一行转成数字再比,索引自然失效。手机号、订单号、编号这类字符串字段,条件值永远加引号。JOIN 两边类型不一致同样会触发转换,写 SQL 时显式对齐类型。
## 三、前导通配符
```
SELECT * FROM article WHERE title LIKE '%优化%'; -- 用不上普通索引
```
B+ 树按前缀有序排列,通配符在最前面就没法确定起点。前缀匹配 LIKE '关键词%' 没问题;确实要做包含匹配,考虑全文索引或搜索引擎方案,普通索引帮不上忙。数据里真的需要包含匹配,把搜索拆给专门组件,业务库保持简单索引。
## 四、联合索引顺序不对
联合索引 (a, b) 先按 a 排序、再按 b 排序,条件里只有 b 时用不上它:
```
ALTER TABLE orders ADD INDEX idx_status_created (status, created_at);
-- WHERE status = 1 AND created_at > ... :能用上
-- 只有 created_at 条件:用不上
```
原则是最左前缀:查询条件要从索引最左列开始连续命中。设计联合索引时,等值条件、区分度高(唯一值多)的列放左边,范围条件的列放右边。顺序不合适就新建一个正确的联合索引,旧索引确认无人引用后再删,不要原地赌优化器。
拿不准就走一遍 EXPLAIN,key 列是不是 NULL、rows 列是多大,结果不会骗人,比背规则更直接。多数场景里改写比加索引见效更快,先改写、再看要不要建索引,是更省资源的顺序。