explain 显示 type=all 说明 mysql 正在全表扫描,已存在性能问题;需检查索引缺失、字段类型不匹配、单列索引在复合查询中失效等问题,并优先建立合适顺序的联合索引或覆盖索引。

为什么 EXPLAIN 显示 type=ALL 就该警觉
这代表 MySQL 正在全表扫描,哪怕只有几千行,一旦并发上来或数据增长,查询就会明显卡顿。不是“可能慢”,是“已经慢了”,只是你还没压测到那个点。
常见错误现象:SELECT * FROM orders WHERE user_id = 123 响应超 500ms,但表才 2 万行;EXPLAIN 输出里 type 列是 ALL,key 列是 NULL。
- 先确认 WHERE 条件字段是否已有索引:查
SHOW INDEX FROM orders,看user_id是否在Key_name列出现 - 没索引就加:执行
ALTER TABLE orders ADD INDEX idx_user_id (user_id)(注意别在高峰期直接建) - 已有索引但没生效?检查字段类型是否一致——比如查询用字符串
'123',而user_id是INT,隐式转换会让索引失效 - 复合查询时,单列索引可能被忽略:如
WHERE user_id = 123 AND status = 'paid',优先考虑联合索引(user_id, status),顺序不能颠倒(等值条件字段放前面)
ORDER BY + LIMIT 为什么有时不走索引
不是所有排序都能靠索引加速。MySQL 只有在索引的顺序恰好匹配 ORDER BY 字段,且 WHERE 条件能利用该索引前缀时,才会用索引排序;否则会回表+文件排序(Using filesort)。
使用场景:分页查最新订单 SELECT id, title FROM articles WHERE category_id = 5 ORDER BY created_at DESC LIMIT 20,但响应越来越慢。
- 检查
EXPLAIN的Extra列是否含Using filesort - 确保索引覆盖排序字段和过滤字段:建
INDEX idx_cat_created (category_id, created_at),注意顺序——category_id是等值,放前;created_at是范围/排序,放后 - 避免在
ORDER BY字段上用函数:如ORDER BY DATE(created_at)会直接让索引失效 - 如果
SELECT字段太多,即使走了索引,也可能因回表开销大而变慢;可考虑覆盖索引:把常用返回字段也加进索引末尾,如(category_id, created_at, id, title)
SELECT * 和索引覆盖之间的冲突怎么解
加了索引却没变快,大概率是因为 SELECT * 强制 MySQL 回表取其他字段,把索引的优势抵消了。索引本身快,但“查完索引再翻原表”可能比全表扫还慢。
性能影响:一张 100 万行的表,主键索引 B+ 树高度为 3,但回表要额外做 20 次随机 IO(每行一次),远慢于顺序读全表。
- 改写查询,只取必要字段:把
SELECT *换成SELECT id, name, status,再配合覆盖索引 - 建覆盖索引时,把
SELECT中所有字段都包含进去(顺序无关,但建议按使用频率排):如INDEX idx_cover (user_id, status, created_at, id, name) - 注意索引宽度:字符串字段别无脑
VARCHAR(255)全长参与索引,用VARCHAR(50)或前缀索引(如email(32))控制大小 - 覆盖索引对
UPDATE/INSERT有写放大影响,别给不常查的字段加进索引
线上加索引卡住 DML 怎么办
MySQL 5.6+ 默认支持在线 DDL,但不是所有操作都真正“在线”。比如在大表上加普通索引,仍会锁表(尤其是 ALGORITHM=COPY 时),导致业务 INSERT/UPDATE 阻塞。
容易踩的坑:运维同学在从库上执行 ALTER TABLE ... ADD INDEX,结果主库延迟飙升,因为从库重放 DDL 期间无法应用后续 binlog。
- 加索引前先看 MySQL 版本:5.6 起支持
ALGORITHM=INPLACE,但需显式指定;8.0 默认更友好,但仍建议加ALGORITHM=INPLACE, LOCK=NONE - 用
pt-online-schema-change(Percona Toolkit)代替原生命令,它通过影子表+触发器实现无锁变更 - 避开高峰时段,提前在从库验证耗时:执行
ALTER TABLE t1 ADD INDEX idx_xxx (col) ALGORITHM=INPLACE, LOCK=NONE;,观察SHOW PROCESSLIST是否长时间卡在altering table - 加完立刻验证:跑一遍原 SQL +
EXPLAIN,确认key列非NULL,且rows显著下降
索引不是越多越好,每个索引都是写操作的负担、内存的占用、优化器选择的干扰项。上线前最该问一句:这个索引,有没有被真实查询用到过?











