先说结论:同一套机器、同一份数据、同一个压测脚本,原生 JDBC 一定比绝大多数 ORM 快,但快多少,完全取决于你让 ORM 帮你做了哪些事。这次 ORM 性能测试 Benchmark 我前前后后跑了五轮,中间推翻了三套方案,最后才把结果稳定到可以对外解释的程度。最终版的数据不是为了让某个框架“丢人”,而是回答一个几乎所有团队都会遇到的实际问题:我们到底该不该为了性能,把项目从 JPA/MyBatis 切到手动 SQL 更重的框架?
如果你是技术负责人、架构师,或者正在给团队做 ORM 选型调研,这份记录会非常有用。文中不会只丢一张跑分表,还会把测试环境、表结构、场景设计、统计口径、隐藏的配置差异全部摊开。因为踩过太多次“换个环境结果完全反过来”的坑,我越来越确认一件事:ORM Bench 里最难的从来不是跑代码,而是让所有人承认,你比的往往是配置、SQL 写法和模型设计,而不是框架本身。
1. 为什么是“最终版”:问题出在跑分设计本身
1.1 之前两版结果为什么没人敢用
第一版结果出来的时候,团队里第一个反应不是“哦原来 JPA 这么慢”,而是“你这数据我不信”。当时用的是 JMH,写起来很顺手,代码也短,但跑了两轮出来的方差大到离谱。JDBC 同一个查询,P99 抖动超过 30%,MyBatis 甚至有时比 JDBC 还快,有时慢三倍。问题根本不在于 CPU 或内存,而是连接池、GC、数据库连接的建立时间、系统冷热页缓存这些外部因素全混在里面。
第二次我吸取了教训,换成 Shell 脚本独立进程跑,每个框架一个进程,不共享 Spring 上下文。可还是有问题:连接池初始是懒创建的,前几百次请求全在建立新连接,日志里能看到 1 秒以上的毛刺;范围查询固定从 offset=0 开始,导致所有请求都压在 InnoDB B+ 树的同一小块数据上,缓存命中率是 100%,跟真实业务完全不像。这两版结果发出去,评审第一句话就是:你的预热去哪了?你的随机偏移呢?数据为什么看着像玩具?
所以第三版开始,我不再急着写代码,而是先写测试设计文档。把“有没有预热”“连接怎么创建”“事务边界怎么切”“query 命中的是不是热点页”“返回结果集多大”全部明确,再动手。最终版之所以叫最终版,不是因为我以后再也不测 ORM,而是这套方案反复跑了十轮,中位数的相对误差稳定在 5% 以内,至少敢拿出去当选型依据了。
1.2 最终版只回答三个问题
这次跑分不要贪多,会问自己三个问题:
第一,单表 CRUD 这种最常见的业务操作,ORM 比 JDBC 慢多少?如果只有 10%~20%,那开发效率换性能完全划算,不用焦虑。第二,批量写入和分页查询这种容易“踩雷”的场景,慢的到底是谁?是 ORM 本身,还是框架默认把很多工作做在了你看不到的地方?第三,如果我用五种框架写“等价”的功能,返回同样 JSON 数据,最终瓶颈会出现在什么环节?是 SQL 生成与结果映射,还是数据库真正执行的 SQL 次数?
带着这三个问题,就不会再出现“MyBatis 天下第一”这种结论。因为同一套业务里,不同 ORM 可能会生成不同形态的 SQL,有的是一条大 SQL,有的是几十条小 SQL;有的自动帮你 count 一遍,有的不会。这些都会在数据上产生数量级的差异,可比性就需要设计得足够细。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 测试设计:先把变量关进笼子再谈数据
2.1 被测对象、版本和环境怎么锁
版本不锁死,Benchmark 等于白做。比如 Hibernate 6.x 比 5.x 在很多查询路径上优化过不少;MyBatis 不同版本对批处理 Executor 的行为也有差异。最终版锁定了下面这套环境:
| 组件 | 版本/配置 | 说明 |
|---|---|---|
| 操作系统 | Ubuntu 22.04 LTS | 和生产容器同大版本 |
| CPU / 内存 | 8 核 16 线程 / 32 GB | 压测应用和数据库分开部署 |
| Java | OpenJDK 17.0.8 | 常见的服务端 LTS 版本 |
| MySQL | 8.0.33 | 生产版本,same bufffer pool 4G |
| 连接池 | HikariCP 5.0.1 | maximumPoolSize=20,minimumIdle=20 |
| JDBC 驱动 | mysql-connector-j 8.1.0 | 所有框架共用同一个驱动版本 |
被测对象一开始就定成五个:原生 JDBC 作为基线,接着是 MyBatis 3.5.13、MyBatis-Plus 3.5.3.2、Spring Data JPA(底层 Hibernate 6.2.13)、jOOQ 3.18.2。没有纳入 Ebean、QueryDSL、Exposed,是因为团队当前候选就这几个,跑分要给选型服务,不是给开源社区做排行榜。
版本选择还有个容易被忽略的地方:Spring Boot 一旦把依赖版本统一管理,MyBatis-Plus 3.5.3.2 和 Spring Data JPA
