MySQL 8.4 索引新坑:Skip Scan 别乱用,OPTIMIZE TABLE 可以省了
MySQL 8.4 普及之后,索引机制确实带来了一些新优化。但实际排查慢 SQL 时你会发现:索引失效的核心坑点一个没少,反而多了两个版本专属的避坑点。
这篇文章就当一份工程师笔记,把这几个坑记清楚。
坑 1:索引不是越多越好
这个坑从 5.x 时代就有,8.4 也没变。
很多人认为“查询慢就加索引”,于是每个字段都塞进索引。但索引会直接拉低 INSERT / UPDATE / DELETE 的效率,因为每次写入都要同步维护索引 B+ 树。
生产环境中,单表索引建议不超过 5 个。超过这个数,写入性能基本可以肉眼感知到下降。
坑 2:别依赖 8.4 的 Skip Scan
MySQL 8.4 开始支持跳过扫描(Skip Scan),允许查询跳过联合索引的中间列直接走索引。听起来很美好,但限制非常严格:
- 仅限等值查询
- 字段选择性必须高
这两条在生产环境很难同时满足。一旦选择性不够,优化器根本不会走 Skip Scan,索引照样失效。
所以,设计联合索引时仍然以最左前缀匹配为准。Skip Scan 只能当成一个“偶尔能捞一手的补充”来看待,不能作为设计依据。
坑 8:8.4 的索引碎片自动清理,别手动 OPTIMIZE TABLE
MySQL 8.4 及以上版本对索引碎片有了自动清理机制,这意味着:不再需要手动执行 OPTIMIZE TABLE。
很多人还在沿用旧习惯,定期去跑 OPTIMIZE TABLE 维护表。在 8.4 上这反而是一个坑:OPTIMIZE TABLE 会锁表,生产环境一旦执行,直接阻塞写入。
版本差异要清楚:
- MySQL 8.4 及以上:碎片自动清理,无需手动 OPTIMIZE
- MySQL 8.0 及以下:依然需要根据实际情况手动维护
升级到 8.4 之后,请把定时任务里的 OPTIMIZE TABLE 删掉。
慢 SQL 优化的核心
90% 的慢 SQL 是索引用错,不是没索引。
所以优化的重点不是盲目加索引,而是用 EXPLAIN 找到索引失效的根因,再做针对性调整。
EXPLAIN 输出中,重点看四个点:
- type:ALL 表示全表扫描,最慢;至少要达到 range 或 ref
- key:实际命中的索引。如果为 NULL,说明索引没被使用
- rows:预估扫描行数,数值越大越危险
- Extra:Using filesort 表示排序没走索引;Using temporary 会创建临时表,性能极差;Using index 是最优的覆盖索引
只要发现 type=ALL、key=NULL、Extra 里出现 filesort 或 temporary,基本可以断定这条 SQL 的索引设计有问题。
取舍总结
| 项目 | 结论 |
|---|---|
| 索引数量 | 单表不超过 5 个,宁缺毋滥 |
| Skip Scan | 8.4 支持,但仅限等值查询且字段选择性高,生产环境切勿依赖 |
| 索引碎片 | 8.4 及以上自动清理,手动 OPTIMIZE TABLE 反而造成锁表 |
| 慢 SQL 判断 | 依赖 EXPLAIN,而不是靠猜 |
MySQL 8.4 的优化确实有,但版本升级不改变一个事实:索引设计要回到最左前缀匹配这条基本线上,配合 EXPLAIN 验证,而不是相信某个新特性可以偷懒。