explain显示type=all需警惕,因mysql正全表扫描,数据量大时响应慢;主因是索引未命中最左前缀、函数或隐式类型转换致失效;应检查key是否NULL、rows是否接近总行数,并优化查询条件与索引设计。

为什么 EXPLAIN 显示 type=ALL 就该警惕
这代表 MySQL 正在对整张表逐行扫描,尤其在数据量上万后,响应会明显变慢。常见诱因不是没加索引,而是查询条件没命中索引最左前缀、用了函数或隐式类型转换,导致索引失效。
实操建议:
- 用
EXPLAIN select ...看key列是否为NULL,rows是否接近表总行数 - 检查
WHERE中字段是否在复合索引的最左侧位置,比如索引是(a, b, c),但查询只用了b = ?,那就不会走索引 - 避免在索引列上做运算:
WHERE YEAR(create_time) = 2024→ 改成WHERE create_time >= '2024-01-01' AND create_time - 字符串字段用数字查询会触发隐式转换:
WHERE mobile = 13812345678(mobile 是VARCHAR)→ 改成WHERE mobile = '13812345678'
哪些 ORDER BY 会绕过索引排序
即使 WHERE 走了索引,如果 ORDER BY 字段不在索引中,或顺序/方向不一致,MySQL 仍可能触发 using filesort,严重时连带放弃索引范围扫描。
实操建议:
- 复合索引
(a, b, c)可支持ORDER BY a, b或ORDER BY a DESC, b ASC(注意:MySQL 8.0+ 才支持混合方向;5.7 只支持全 ASC 或全 DESC) -
ORDER BY a, c无法利用(a, b, c)索引的排序能力,因为跳过了b,中间断层 - 若业务允许,把
SELECT *改成只查必要字段,减少排序开销;更彻底的做法是加覆盖索引,例如INDEX (a, b, status, created_at)来支撑WHERE a=? AND b=? ORDER BY created_at DESC
LIMIT 加得再早,也救不了没索引的 OFFSET
SELECT * FROM t ORDER BY id LIMIT 10000, 20 看似只取 20 行,但 MySQL 仍要先定位到第 10001 行——这意味着它得扫描前 10020 行。偏移越大,越接近全表扫描。
实操建议:
- 用游标替代分页:记录上一页最后的
id,下一页查WHERE id > 12345 ORDER BY id LIMIT 20 - 如必须用
OFFSET,确保ORDER BY字段有索引,且该索引能同时满足WHERE条件(否则先排序再过滤,代价更高) - 对大表分页,可先用子查询缩小主表扫描范围:
SELECT * FROM t JOIN (SELECT id FROM t WHERE status=1 ORDER BY id LIMIT 10000, 20) AS tmp USING (id)
统计类查询为什么总扫全表
count(*) 在 InnoDB 中不直接存总行数,但也不是每次都全扫——关键看有没有可用的二级索引。如果没有,就只能走聚簇索引(也就是整张表);如果有非空索引(比如 INDEX(status)),优化器可能选它来遍历,更快。
实操建议:
- 想加速
COUNT(*),建一个不为空的窄索引,例如INDEX(dummy),其中dummy是定义为NOT NULL的小字段(如TINYINT) - 避免用
COUNT(column)统计非空行,除非真需要判空;COUNT(*)和COUNT(1)在语义和性能上等价 - 实时精确总数本身反模式,高并发场景建议用缓存 + 增量更新,或采样估算(如
SELECT TABLE_ROWS FROM information_schema.TABLES,但它是估算值)
真正难处理的是那些“看起来能走索引、实际却失效”的组合:函数、OR 条件、!=、NOT IN、json 字段查询……这些地方最容易漏掉执行计划验证。