type=ALL表示MySQL执行全表扫描,即未使用索引、逐行匹配;常见原因包括无索引、函数操作、隐式类型转换、联合索引未满足最左前缀或优化器基于成本模型判定全表扫描更优。

能走索引,但必须满足三个硬性条件:WHERE是等值过滤、目标列是索引最左前缀、查询不能带GROUP BY或HAVING。 不符合任一条件,EXPLAIN里type大概率显示ALL,rows等于全表行数——这不是配置问题,是执行路径被优化器主动放弃了。
为什么SELECT MAX(price)不走索引?
常见错误现象是给price建了单列索引,但执行SELECT MAX(price) FROM products时没命中。MySQL 优化器认为:遍历B+树找“最右叶子节点”不如直接全表扫描快,尤其当表不大或缓存足够时。它不是“不会”,而是“算下来不划算”。
- 真正起作用的是索引的物理结构——B+树叶子节点天然有序,
MIN()取最左,MAX()取最右,O(1)级定位 - 但优化器需要明确的“边界提示”,否则默认按最坏情况估算成本
- 单列索引在无WHERE条件下,无法提供这种确定性,所以退化为全表扫描
如何强制让MAX/MIN走索引
核心思路是用一个恒真等值条件“锚定”索引最左列,让优化器确认可以跳转到边界节点。实操上比加FORCE INDEX更稳定。
- 如果
category_id有索引,写WHERE category_id = category_id,MySQL会识别这是恒真且可利用索引前缀的条件 - 更推荐建联合索引:
CREATE INDEX idx_category_price ON products(category_id, price),然后查SELECT MAX(price) FROM products WHERE category_id = 5 - 避免写
WHERE category_id IN (5)——部分旧版本会拒绝使用索引做极值查找 - 不要在
price上用函数,比如WHERE ABS(price) > 100,这直接让索引失效
GROUP BY + MAX/MIN为什么总出现Using temporary
即使你建了(category_id, price)索引,SELECT category_id, MAX(price) FROM products GROUP BY category_id在 MySQL 5.7 及更早版本中依然大概率触发临时表和文件排序。这是因为8.0之前对分组后极值计算的索引优化非常保守。
- MySQL 8.0.13+ 启用了
skip_scan(默认开启),对单列GROUP BY+ 覆盖索引场景有原生支持 - 若仍需兼容低版本,改写为关联子查询:
SELECT DISTINCT p1.category_id, (SELECT MAX(p2.price) FROM products p2 WHERE p2.category_id = p1.category_id) - 注意:子查询里的
p2.category_id = p1.category_id必须能命中索引最左前缀,否则嵌套也没用 - 多列
GROUP BY(如GROUP BY region, category_id)目前仍基本无法避免临时表,别强求
覆盖索引对MAX/MIN生效的关键,从来不是“有没有索引”,而是“优化器能不能确信只需读索引边界”。字段顺序、WHERE写法、MySQL版本这三点卡得极死,漏掉任何一个,Using index就永远不会出现在EXPLAIN的Extra里。


















