一、什么是 rewriteBatchedStatements?
这是 MySQL JDBC 驱动的一个连接参数。默认值为 false。
当设置为 true 时,MySQL 驱动会将多条 INSERT 语句重写为一条批量 INSERT:
sql
-- rewriteBatchedStatements=false(默认)分开发送
INSERT INTO user (name, age) VALUES ('A', 1);
INSERT INTO user (name, age) VALUES ('B', 2);
INSERT INTO user (name, age) VALUES ('C', 3);
-- rewriteBatchedStatements=true 重写为一条
INSERT INTO user (name, age) VALUES ('A', 1), ('B', 2), ('C', 3);
1-1、配置方式
bash
spring:
datasource:
url: jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true
1-2、四种场景对比(10万条数据)
场景一:for 循环 10万次 save(),rewriteBatchedStatements=false
java
for (int i = 0; i < 100000; i++) {
userMapper.save(new User("name" + i, i));
}
底层行为:
- 每次
save()都是一次独立的数据库连接请求(获取连接 → 发送SQL → 提交 → 释放连接) - 10万次 = 10万次网络往返(RTT)
- 无任何批处理
性能: ❌❌❌ 极慢,约 60~120秒 以上
场景二:每1000条 saveBatch(),rewriteBatchedStatements=false
java
List<User> batch = new ArrayList<>();
for (int i = 0; i < 100000; i++) {
batch.add(new User("name" + i, i));
if (batch.size() == 1000) {
userService.saveBatch(batch); // MyBatis-Plus 默认 batchSize=1000
batch.clear();
}
}
底层行为:
- MyBatis-Plus
saveBatch()内部使用了 JDBC 的addBatch()/executeBatch() - 但由于
rewriteBatchedStatements=false,JDBC 虽然是批处理,MySQL 驱动仍然逐条发送给服务器 - 相当于:将 1000 条 SQL 打包后依次发送,减少了获取连接的开销,但每条 SQL 仍单独传输
sql
-- 实际发送给MySQL:
INSERT INTO user VALUES (...); -- 第1条
INSERT INTO user VALUES (...); -- 第2条
...(1000条分开发送)
性能: ✅ 比场景一好很多,约 5~15秒(减少了事务次数)
【注意】:网络请求过程中,可以发送的sql数量也是有限的!
场景三:每1000条 saveBatch(),rewriteBatchedStatements=true ⭐
java
List<User> batch = new ArrayList<>();
for (int i = 0; i < 100000; i++) {
batch.add(new User("name" + i, i));
if (batch.size() == 1000) {
userService.saveBatch(batch);
batch.clear();
}
}
底层行为:
saveBatch()内部使用addBatch()/executeBatch()rewriteBatchedStatements=true生效,MySQL 驱动将 1000 条重写为一条 SQL:
sql
INSERT INTO user (name, age) VALUES
('name0', 0), ('name1', 1), ..., ('name999', 999);
- 只需要 100次网络传输(10万/1000)
- MySQL 服务器端一次性解析、一次性写入,效率极高
性能: ✅✅✅ 最快,约 1~3秒
二、性能对比汇总
| 场景 | 方式 | rewriteBatchedStatements | 网络IO次数 | 预估耗时(10万条) |
|---|---|---|---|---|
| 场景一 | for + save() | false | 10万次 | 60~120s ❌❌❌ |
| 场景二 | saveBatch(1000) | false | 10万次(批量提交) | 5~15s ✅ |
| 场景三 | saveBatch(1000) | true | 100次 | 1~3s ✅✅✅ |
三、核心原理总结
rewriteBatchedStatements 生效的前提条件:
-
JDBC 必须调用 addBatch() + executeBatch()
-
MyBatis-Plus 的 saveBatch() 满足这个条件
-
逐条 save() 不满足,参数无效
saveBatch() 的作用:
- 减少事务提交次数(场景二 vs 场景一)
rewriteBatchedStatements=true 的额外作用:
- 将多条 INSERT 合并为一条,极大减少网络传输和服务器解析开销
最佳实践:saveBatch() + rewriteBatchedStatements=true,batchSize 推荐 500~1000。