Java 17与MySQL:一次慢SQL引发的索引设计思考

阿乐
阿乐 管理员 黑卡会员
发布于 2026-09-06 09:51 ·8 浏览 ·0 回复

最近在负责一个老项目的性能优化,技术栈刚升级到 Java 17,数据库还是 MySQL 8.0。原本以为升级 JDK 就能解决大部分问题,结果上线第二天,监控就报了一条慢 SQL:某个列表接口的查询耗时从平均 80ms 涨到了 2.3 秒。登录 MySQL 一看,`EXPLAIN` 结果里赫然写着 `type: ALL`,全表扫描,扫描行数 120 万。

查询本身并不复杂,就是按用户 ID 和创建时间范围取数据,再关联一张订单表。代码里也加了索引,但问题在于只建了一个单列索引 `idx_user_id`,而查询里用了两个条件。MySQL 优化器最终选择了用户 ID 索引,回表后还需要在内存里过滤几百条记录,再去做关联排序。数据量小的时候无所谓,可一旦超过百万级,这条 SQL 就成了定时炸弹。

一次调整,从 2.3 秒到 30 毫秒

我当时的第一反应是加一个联合索引 `(user_id, create_time)`,把过滤和排序都压在索引里。执行 `ALTER TABLE` 之后,`EXPLAIN` 的 `type` 变成了 `ref`,`Extra` 里也出现了 `Using index condition`。接口耗时直接降到 30ms 左右,效果立竿见影。

但真正让我反思的不是这条 SQL 本身,而是为什么一开始会漏建索引。翻看建表语句,发现最初的开发者只想着“用户查询多,给 user_id 加索引”,却忽略了查询模式里还有一个 `create_time` 的范围条件。这几乎是新手最容易犯的错——以为有了索引就万事大吉,却从不看执行计划。

索引设计的几个底层原则

这次踩坑让我重新梳理了索引设计时应该遵循的原则。第一,尽量覆盖查询中所有的等值条件列,把它们放在联合索引的最前面。对于范围条件列,放在等值列的后面,但要注意它后面的列无法用于索引排序或范围查找,这是 MySQL 的“最左前缀”和“范围截断”特性决定的。

第二,排序字段优先考虑加入索引。如果 `ORDER BY` 的字段不在索引里,MySQL 就要做 filesort。即使数据量不大,filesort 也会消耗 CPU 和内存。上面的例子中,如果查询需要按 `create_time DESC` 排序,那联合索引 `(user_id, create_time)` 就同时解决了过滤和排序,连临时文件都不用建了。

第三,不要迷信“多建索引没坏处”。索引不是免费的,每次 `INSERT`、`UPDATE`、`DELETE` 都需要维护索引树,索引越多,写入越慢。更麻烦的是,一个错误的索引会让优化器误判,反而选错执行计划。最典型的例子就是给低选择性的字段(比如 `status`)建了索引,MySQL 计算后觉得回表成本太高,依然走全表扫,这个索引就成了摆设。

用执行计划替代直觉

Java 17 带来的新特性,比如 `Record`、`switch` 表达式、`Stream.toList()`,确实让代码更简洁了,但它们改变不了数据库的行为。真正影响 SQL 性能的,永远是索引、统计信息、优化器策略这几件事。

我后来养成了一个习惯:每写一条涉及多条件过滤或排序的查询,强制自己先跑一遍 `EXPLAIN`,确认 `type` 至少是 `range`,`key` 用的是预期索引,`rows` 不超过实际需要的 10 倍。如果看到 `Using temporary` 或 `Using filesort`,就必须停下来分析代价。工具链上,像是 MySQL Workbench 的可视化执行计划,或者 Percona Toolkit 里的 `pt-index-usage`,都可以用来排查无效索引,但核心还是对慢 SQL 日志保持敏感。

反思:索引设计是一种建模

很多人把索引认为是“调优阶段的附属品”,于是表结构设计完后就不再思考索引,等到出问题才临时打补丁。但实际上,索引设计应该是数据模型设计的一部分。你设计表字段时,就要预判未来的查询模式——哪些列是等值条件、哪些是范围、哪些需要排序分组、哪些只需要覆盖就能避免回表。这比事后加索引要靠谱得多。

回到前面那条 SQL,如果我当时在建表时就想清楚:系统是“按用户查订单”,且大多带时间范围,完全可以直接把复合索引写进迁移脚本里。可惜现实中业务往往演进得比预期快,所以另一个实用建议是:每次迭代新功能时,顺便检查一下现有索引是否还能支撑新查询,必要时和 DBA 一起评审。

Java 17 解决的只是应用层的效率和开发体验,而数据层的性能,永远需要你对索引怀有敬畏。一次慢 SQL 的教训,比读十篇理论文章都管用。下次再有人告诉你“MySQL 加个索引就快了”,请先让他把执行计划拿出来看看。

全部回复 0

还没有回复,来抢沙发~