索引生效需满足最左前缀、类型一致、无函数干扰三条件;where字段无索引或不匹配是全表扫描主因,explain中key为null且type为all即表明未走索引。

能避免全表扫描,但必须让 WHERE 条件和索引“对得上”——不是建了索引就自动生效,而是每一条 WHERE 子句都要经得起最左前缀、类型一致、无函数干扰这三关。
WHERE 字段没索引或索引不匹配
这是最常见也最容易被忽略的根源。MySQL 不会“猜”你要查什么,它只认执行计划里 EXPLAIN 显示的 key 列是否非空。如果 key 是 NULL,且 type 是 ALL,基本就是全表扫描了。
- 检查高频
WHERE字段是否已建索引:比如status、user_id、create_time这类过滤条件频繁出现,就得单独或联合建索引 - 联合索引必须从最左列连续使用:索引是
(a, b, c),那WHERE a = 1 AND b > 10可以走,但WHERE b = 10或WHERE c = 'x'就完全失效 - 别依赖“字段名看起来像有索引”:用
SHOW INDEX FROM table_name确认真实存在,且不是冗余索引(比如已有(a, b),又单独建了a索引)
WHERE 中用了函数、表达式或隐式转换
这类写法表面看逻辑正确,实则直接让索引变成摆设。MySQL 无法把函数结果反向映射到索引 B+ 树节点上,只能退化为逐行计算比对。
-
WHERE YEAR(create_time) = 2024→ 改成WHERE create_time >= '2024-01-01' AND create_time -
WHERE mobile = 13812345678(mobile是VARCHAR)→ 改成WHERE mobile = '13812345678',否则触发隐式类型转换 -
WHERE price * 1.06 > 100→ 改成WHERE price > 100 / 1.06,把运算移到常量侧 -
WHERE name LIKE '%abc'→ 无法走索引;若业务允许,改用name LIKE 'abc%'或考虑前缀索引 + 全文检索
OR、!=、IS NULL 等操作符导致索引跳过
这些条件本身不具备高效范围定位能力,优化器往往判定“走索引还不如扫一遍快”,尤其当数据分布较均匀时。
-
WHERE status != 0很可能全表扫描;换成WHERE status IN (1, 2, 3)(如果状态值有限)更稳妥 -
WHERE a = 1 OR b = 2:只有a和b各自有单列索引,且优化器认为合并成本低时才可能走索引;更可靠的是拆成UNION或改用IN覆盖主键 -
WHERE col IS NULL:即使col有索引,也可能不走;建议用默认值替代NULL,比如设为0或'',再查WHERE col = 0 -
WHERE col NOT IN (1,2,3):容易引发全表扫描,特别是右侧列表含NULL时;优先考虑NOT EXISTS或重写逻辑
索引提示(USE INDEX / FORCE INDEX)不是万能解药
加提示只是告诉优化器“用这个索引”,但它不能修复底层不匹配的问题。如果 WHERE 条件根本不符合该索引的最左前缀,或者字段类型不一致,提示只会报错或被忽略。
-
SELECT * FROM t USE INDEX (idx_a_b) WHERE b = 1→ 无效,因为b不在索引idx_a_b的最左位置 -
FORCE INDEX强制使用某索引,但若该索引列在 WHERE 中未出现,MySQL 会直接报错Unknown index - 真正该用提示的场景极少:比如优化器因统计信息陈旧误选了差索引,且你已确认另一索引更优;但首选仍是更新统计信息(
ANALYZE TABLE t)或修正 WHERE 写法
最易被忽略的一点:索引是否生效,不取决于你“觉得它该生效”,而取决于 EXPLAIN 输出中 key 是否非空、rows 是否显著小于表总行数。哪怕只差一个引号、一个函数、一个顺序错位,索引就形同虚设。











