EXPLAIN中key为NULL说明未走索引;需结合type(ALL为全表扫描)、rows(扫描行数是否远小于总数)判断;联合索引须满足最左前缀,函数、隐式转换、NOT、前导%等均会导致索引失效。

EXPLAIN 看到 key 为 NULL 就说明没走索引
这是最直接的判断依据,不用猜、不靠经验。在任意 SQL 前加 EXPLAIN,看输出中 key 列是否为 NULL。是,则索引未被选用;不是,则至少有某个索引参与了扫描。
注意:即使 key 非空,也要看 type(比如 ALL 表示全表扫描,range 或 ref 才算有效利用)和 rows(预估扫描行数是否远小于表总行数)。
常见误判点:
- 表太小(比如只有几行),优化器认为全表扫描比走索引还快,
key也会是NULL - 用了
SELECT *+ 覆盖索引不全,触发回表但代价高,优化器主动弃用 - 统计信息过期(
ANALYZE TABLE可刷新)导致成本估算失真
联合索引不满足最左前缀,后面字段全作废
假设你建了联合索引 KEY idx_code_age_name (code, age, name),那只有以下条件能用上它:
WHERE code = ?WHERE code = ? AND age = ?WHERE code = ? AND age = ? AND name = ?-
WHERE code = ? AND name = ?(age缺失,但code存在,仍可用前缀;不过name无法走索引查找,只能当过滤条件)
这些写法一定失效:
-
WHERE age = ?(跳过code) -
WHERE name = ?(跳过前两列) -
WHERE age = ? AND name = ?(无code,完全不匹配前缀)
本质是 B+ 树按索引定义顺序排序,没有起点就无法定位范围。
WHERE 里对索引列用函数或运算,索引直接“看不见”
索引里存的是原始值,不是计算结果。只要你在 WHERE 中对索引列做任何变换,MySQL 就没法拿索引树里的值去比对。
典型失效写法:
-
WHERE DATE(create_time) = '2024-04-21'→ 改成WHERE create_time >= '2024-04-21' AND create_time -
WHERE UPPER(name) = 'SUNYANG'→ 改成WHERE name = 'sunyang'(注意大小写) -
WHERE price * 1.1 > 100→ 改成WHERE price > 100 / 1.1 -
WHERE id + 1 = 100→ 改成WHERE id = 99
哪怕只是 IFNULL(col, 'default') 或 COALESCE(col, 'x'),也会让该列索引失效。
隐式类型转换和 NOT 类操作让优化器放弃索引
字符串字段查数字、数字字段查字符串,MySQL 会悄悄加一个类型转换函数,等价于你在列上写了函数——索引失效。
例如:user_id 是 INT,但你写 WHERE user_id = '123',实际执行相当于 WHERE CAST(user_id AS CHAR) = '123',索引失效。
同理,这些操作也大概率失效(不是绝对,但极难命中):
-
WHERE status != 1或WHERE status 1 WHERE name NOT IN ('a', 'b')-
WHERE age IS NOT NULL(IS NULL可走索引,IS NOT NULL不行) -
WHERE name LIKE '%三'或WHERE name LIKE '%三%'(前导 % 无法定位起始位置) -
WHERE a = 1 OR b = 2(若b没索引,或a、b不同索引,通常退化为全表)
真正棘手的是:这些写法在数据量小时看不出问题,一到千万级表,慢得毫无征兆。


















