MySQL的MIN()/MAX()函数在满足三点条件下可走索引:WHERE为等值过滤、目标列为索引最左前缀、无GROUP BY/HAVING;否则常退化为全表扫描,因优化器认为其成本更低。
MySQL 的
和
走不走索引?看这三点
能走,但只在非常明确的条件下走——不是所有带索引的列都自动生效。核心就三条:
条件必须是等值过滤(
或
),目标列必须是索引最左前缀,且不能有
或
打乱执行路径。
常见错误现象:
给
列加了单列索引,执行
却没用上索引,
显示
为什么这样设计:
MySQL 优化器认为全表扫描比遍历 B+ 树叶子节点更省事——因为单列索引的叶子是有序的,但引擎仍需定位到“最后一个”节点,而全表扫描可能更快(尤其小表)
实操建议:
强制走索引的可靠方式是加上一个恒真等值条件,比如
(需该列有索引),或更稳妥地建联合索引:如果常查
且按
过滤,就建
联合索引里
/
要怎么排序才有效
顺序决定一切。索引定义是
,那
在
下才可能走索引;如果只写
,整个索引就废了。
使用场景:
统计某类商品最低售价:
→ 索引应为
,而不是
参数差异:
和
对索引方向无要求,B+ 树天然支持双向遍历;但如果你同时要
,那就得看索引顺序是否匹配排序方向
性能影响:
用对了,执行计划里
显示只用了索引前缀,
接近 1;用错了,
就是全表行数,还可能触发临时表和文件排序
+
/
索引为啥经常失效
因为 MySQL 在 8.0 之前对这类聚合的索引优化很保守,即使有合适索引,也倾向用临时表分组再计算极值。
MySQL(Linux)
MySQL 9.6.0是面向Linux平台的2026年创新版本,核心架构迎来重大革新。其将外键约束与级联操作从InnoDB引擎层上移至SQL层,确保所有数据变更均被完整记录至Binlog,彻底解决了CDC(变更数据捕获)与主从复制中的数据不一致难题。此外,该版本引入container_aware启动选项以原生适配容器环境,并对审计日志进行了组件化重构,为追求极致数据一致性与云原生体验的开发者提供了全新选择。
下载
常见错误现象:
,哪怕有
索引,
仍显示
实操建议:
升级到 MySQL 8.0+,并确认
中启用了
(默认开启);或者改写成关联子查询:
,配合
索引可命中
兼容性影响:
5.7 及更早版本基本无法避免临时表;8.0.13+ 对单列
+ 覆盖索引有 Skip Scan 优化,但多列
仍大概率退化
别忽略
和数据分布对索引选择的影响
哪怕索引结构完全正确,只要字段大量为
,或者极值集中在少数几个值里,优化器也可能放弃走索引。
容易踩的坑:
建了
索引,想查
,结果发现
字段 95% 都是
,优化器直接判定走索引比全表扫描还慢
实操建议:
用
查
值,如果
的基数远低于总行数(比如 10 行里只有 2 个不同值),就别指望靠它驱动索引扫描;此时更适合建函数索引(8.0.13+):
为什么重要:
优化器估算成本时,会把
当作独立值参与基数统计,但实际 B+ 树中
的存储和比较逻辑特殊,容易导致估算偏差,进而选错执行计划
索引对
/
是否生效,从来不是“有没有索引”的问题,而是“查询条件能不能把索引用成一条线性扫描路径”的问题。最容易被忽略的是:你以为的等值条件,在隐式类型转换或字符集不一致时,早就悄悄让索引失效了。
MIN()MAX()WHERE=INGROUP BYHAVINGpriceSELECT MAX(price) FROM productsEXPLAINtype: ALLWHERE category_id = category_idMAX(price)category_id(category_id, price)MIN()MAX()(a, b, c)MIN(b)WHERE a = ?WHERE c = ?SELECT MIN(price) FROM products WHERE category_id = 5(category_id, price)(price, category_id)MIN()MAX()ORDER BY ... LIMIT 1key_lenrowsrowsGROUP BYMIN()MAX()SELECT category_id, MAX(price) FROM products GROUP BY category_id(category_id, price)EXPLAINUsing temporary; Using filesortoptimizer_switchskip_scanSELECT DISTINCT p1.category_id, (SELECT MAX(p2.price) FROM products p2 WHERE p2.category_id = p1.category_id)(category_id, price)GROUP BYGROUP BYNULLNULL(status, created_at)MIN(created_at) WHERE status = 'active'status'active'SHOW INDEX FROM table_nameCardinalitystatusCREATE INDEX idx_active_min ON products ((IF(status = 'active', created_at, NULL)))NULLNULLMINMAX