type=all 确诊全表扫描,说明优化器未使用索引;即便建了索引、写了where,只要type=all出现,即索引被主动放弃,常见原因包括索引列上使用函数、选择性差导致回表成本过高。

EXPLAIN 显示 type=ALL 就是确诊全表扫描
看到 type=ALL 别犹豫,这就是 MySQL 明确告诉你“没走索引”,每次执行都得扫完整张表。它不是警告,是铁证——哪怕你建了索引、字段也写了 WHERE,只要 type=ALL 出现,说明优化器压根没考虑用索引。
常见误判点:
-
key字段为空 ≠ 没建索引,而是优化器主动放弃(比如status只有 3 个值,查'paid'返回 40% 行数,回表成本高于扫表) - 执行计划里
rows接近表总行数,且Extra含Using where,基本可断定是全表扫描 - 别信“我加了索引就该快”——索引存在 ≠ 被用,必须看
EXPLAIN结果
WHERE 条件写法让索引“视而不见”
索引失效不是因为没建,而是条件写法和索引结构对不上。最常踩的坑集中在几个具体操作上:
- 在索引列上用函数:
WHERE YEAR(create_time) = 2024→ 改成WHERE create_time >= '2024-01-01' AND create_time - 隐式类型转换:
mobile是VARCHAR,却写WHERE mobile = 13812345678→ 改成WHERE mobile = '13812345678' - 违反最左前缀:联合索引
(a, b, c),WHERE b = 10或WHERE c = 'x'都不走索引;WHERE a = 1 AND c = 'x'只能用上a,c无法跳过b - 模糊查询写成
LIKE '%abc'→ 无法走索引;右模糊LIKE 'abc%'可以
OR、!=、IS NULL 这类操作符自带“全表扫描倾向”
它们本身不具备高效范围定位能力,优化器常判定“扫一遍比走索引还快”,尤其当数据分布均匀时:
-
WHERE status != 0→ 若状态值有限(如只有 0/1/2),改用WHERE status IN (1, 2) -
WHERE a = 1 OR b = 2→ 如果a和b各有单列索引,拆成SELECT ... WHERE a = 1 UNION ALL SELECT ... WHERE b = 2更可靠 -
WHERE col IS NULL→ 即便col有索引,也可能不走;建议设默认值(如0或''),查WHERE col = 0 -
WHERE id NOT IN (1, 2, NULL)→ 右侧含NULL会导致整个条件恒为 FALSE,且易触发全表扫描;优先用NOT EXISTS或重写逻辑
相关子查询是隐藏的全表扫描放大器
相关子查询不是执行一次,而是外层每返回一行,内层就重跑一遍——如果外层返回 1000 行,内层又没索引,就是 1000 次全表扫描:
- 典型信号:
EXPLAIN中出现DEPENDENT SUBQUERY,且对应行的rows值远大于外层预估行数 -
key_len明显偏小(比如联合索引三列,只用了第一列),说明子查询条件根本没命中索引 - 单独测子句很快(
Elapsed: 00:00:00.01),合到主查询后逻辑读暴涨上万——组合逻辑彻底改变了执行代价 - 能重构尽量重构:用
JOIN替代相关子查询;实在不行,加/*+ USE_INDEX() */HINT 强制走索引,但效果不稳定
ANALYZE TABLE 没更新,优化器仍可能误判索引成本,选错执行路径。










