能,硬性前提是单列或复合索引中极值字段位置可直接定位,且WHERE条件不破坏索引有序性;EXPLAIN中需见“Select tables optimized away”或“Using index for group-by”,rows为1且无Using temporary/filesort。

MAX/MIN 走索引的硬性前提
MySQL 和 PostgreSQL 等主流数据库能对 MAX() 和 MIN() 做索引优化,但不是“有索引就能用”,而是必须满足两个刚性条件:单列索引或复合索引中极值字段位置可直接定位 + WHERE 条件不破坏索引有序性。
- ✅ 正确示例:
SELECT MAX(created_at) FROM orders WHERE status = 'shipped'→ 需要INDEX idx_status_created (status, created_at);优化器能跳到第一个status = 'shipped'的叶子节点,直接取created_at值 - ❌ 错误示例:
SELECT MAX(created_at) FROM orders WHERE user_id = 123→ 若只有INDEX(created_at)或INDEX(user_id),无法协同定位,大概率全表扫描 - ⚠️ 注意:字段定义为
NOT NULL时,优化器更倾向启用INDEX FULL SCAN (MIN/MAX);若允许NULL,且数据中存在大量NULL,可能退化为普通索引扫描
EXPLAIN 里怎么确认真用了索引
别只看 key 列非空——那是基础门槛。真正走极值索引优化的信号藏在 Extra 和 rows 里:
-
Extra出现Using index for group-by(GROUP BY 场景)或Select tables optimized away(如SELECT MIN(id) FROM t且表非空),才是确认信号 -
rows显示为1或极小固定值(比如2、3),而非几万几十万 - 绝对不能出现
Using temporary或Using filesort;如果看到Using where; Using index但rows很大,说明只是覆盖索引避免回表,没触发极值定位
GROUP BY + MIN/MAX 容易踩的坑
很多人以为给分组字段和聚合字段分别建索引就够了,结果执行计划依然走临时表。根本问题是:MySQL 默认不按索引顺序逐组读取,除非明确告诉它“我能跳着查”。
- ✅ 必须建联合索引,且顺序严格为
(group_col, agg_col),查询写成SELECT group_col, MIN(agg_col) FROM t GROUP BY group_col - ❌
INDEX(group_col)+INDEX(agg_col)是无效组合;SELECT MIN(agg_col), group_col(字段顺序颠倒)也会让优化器放弃索引下推 - ⚠️ 即使索引结构正确,若
group_col区分度极低(如只有 3 个枚举值),仍可能因扫描大量重复键值导致逻辑读飙升——这时得结合业务判断是否值得优化
为什么不能写成 ORDER BY + LIMIT
看似等价的写法:SELECT created_at FROM orders ORDER BY created_at DESC LIMIT 1,实际性能可能差一个数量级。
- 该写法强制走
INDEX RANGE SCAN或INDEX FULL SCAN,需排序全部匹配行再取头尾,无法利用 B+ 树最左/最右叶子节点的物理位置优势 -
MAX(created_at)在命中合适索引时,执行路径是:Root → Branch → 最右 Leaf Block,通常仅需 3–4 次逻辑读 - 尤其在带 WHERE 条件时(如
WHERE status = 'done'),ORDER BY ... LIMIT 1往往触发Using filesort,而MAX()可直接定位到符合条件的边界节点


















