为什么 EXPLAIN
显示 type=ALL
就该警惕
这表示 MySQL 正在执行全表扫描,即逐行读取整张表来匹配条件。哪怕只有几万行,一旦并发上来或字段含大文本,响应就会明显变慢。常见诱因是
WHERE条件列没索引、用了函数(如
WHERE YEAR(created_at) = 2023)、或隐式类型转换(比如对字符串字段传数字值)。 用
EXPLAIN FORMAT=TREE查看更直观的执行路径,注意
rows和
filtered的乘积是否远超实际返回行数 避免在索引列上使用
!=、
NOT IN、
LIKE '%abc'—— 这些基本无法走索引 联合索引要注意最左前缀:如果建了
(a, b, c),那
WHERE b = ?或
WHERE c = ?是不会命中索引的
如何判断该加单列索引还是联合索引
单列索引适合高频独立查询的字段,比如
user_id、
status;联合索引则要按「查询频率高 + 过滤性强 + 出现在
WHERE最左侧」的顺序排列。MySQL 8.0+ 支持降序索引,但
ORDER BY a ASC, b DESC需显式声明
(a ASC, b DESC)才能完全覆盖排序。 优先把等值条件列放前面,范围查询(
>,
BETWEEN)放后面,
ORDER BY列尽量接在最后 例如查询
SELECT * FROM orders WHERE shop_id = ? AND status = ? ORDER BY created_at DESC,推荐索引为
(shop_id, status, created_at)别盲目给所有
WHERE字段都建索引——索引越多,写入越慢,且优化器可能选错执行计划
SELECT *
和覆盖索引的关系
如果一个查询的所有字段都能从索引中直接获取(即「覆盖索引」),MySQL 就不必回表查聚簇索引,性能提升显著。但前提是这些字段必须全部出现在同一个索引的「叶子节点」里。
例如有索引(user_id, email, created_at),那么
SELECT user_id, email FROM users WHERE user_id = ?就是覆盖索引查询 但
SELECT *几乎不可能被覆盖,因为主键以外的字段(尤其是
TEXT、
BLOB)不会存进二级索引 用
EXPLAIN看
Extra列是否含
Using index,有就是覆盖了;若出现
Using index condition,说明用了 ICP(索引条件下推),也比全表扫描强
哪些场景下索引会失效或被忽略
即使建了索引,MySQL 也可能不走它。典型情况包括:统计信息过期、索引选择性太低(比如
gender只有 'M'/'F')、查询返回大量行(优化器认为全表扫描反而更快)、或者存在更优的索引被误选。 运行
ANALYZE TABLE table_name更新统计信息,尤其在大批量导入后 对低基数字段(如状态码、开关标志)建索引意义不大,除非配合高选择性条件(如
WHERE status = 'processing' AND updated_at > NOW() - INTERVAL 1 HOUR) 用
FORCE INDEX是临时手段,不能替代问题定位;真正要查的是为什么优化器没选对——通常得结合
SHOW INDEX和
EXPLAIN对比评估
EXPLAIN SELECT id, name FROM users WHERE deleted = 0 AND created_at > '2024-01-01';
索引设计不是一劳永逸的事。表结构变、数据分布变、查询模式变,原来高效的索引可能变成瓶颈。重点不是堆索引,而是理解每条慢查询背后的访问路径和数据特征。
