发布于2026-07-09 阅读(0)
扫一扫,手机访问
### 关键开关:rewriteBatchedStatements=true
很多人以为用了 `addBatch()` 就自动享受到批量插入的性能红利了,这是个常见的误解。MySQL 默认情况下,会把 batch 里的语句当作多条独立的 SQL,老老实实地逐条串行执行,这样 `addBatch()` 带来的只是内存里多缓存了几条语句而已,对性能提升几乎为零。
真正的魔法在于 JDBC URL 里的这个参数:`rewriteBatchedStatements=true`。只要显式开启它,驱动才会把结构相同的 INSERT 语句重新“拼接”成一条包含多个 value 的语句:`INSERT INTO t VALUES (...), (...), (...)`。这才是批量插入性能提升的核心机制。
使用时有几个小陷阱需要注意:
* **URL 示例:** `jdbc:mysql://localhost:3306/db?rewriteBatchedStatements=true&useServerPrepStmts=false`
* 参数 `useServerPrepStmts=false` **必须关掉**。因为服务端预编译不支持 `multi-value INSERT`,开着这个开关,重写机制就失效了。
* 这个参数目前**只对 INSERT 语句生效**,UPDATE 和 DELETE 是享受不到这个优化的。
### 事务粒度:比 batch size 更关键的因素
很多人的优化策略都盯着 batch size,却忽略了事务控制这个更核心的变量。如果你每执行一次 `executeBatch()` 就提交一次事务,数据库的 I/O 和日志刷盘开销会迅速吃掉你所有优化收益。
正确的做法是把多个 batch 包在一个大事务里,累计几千甚至上万条后再一次性提交。
* 操作前调用 `connection.setAutoCommit(false)`,手动控制事务边界。
* 建议每 1000 到 5000 条执行一次 `executeBatch()`,但先不提交,直到数据全部写完,或者达到内存能安全容纳的阈值后再 `commit()`。
* 出错时记得用 `connection.rollback()` 回滚。另外要注意 `executeBatch()` 返回的 `int[]` 数组里,如果某条语句执行失败,会填上 `Statement.EXECUTE_FAILED`,需要对这个返回值做检查。
### 别忘了参数绑定的隐性成本
高频调用 `setString()`、`setLong()` 之类的绑定方法,底层都有反射和类型校验的开销。如果字段个数固定且数据类型简单,可以试试用 `setObject(int, Object)` 来替代具体类型方法,减少反射。更激进的玩法是直接用 `ja va.sql.Statement` + 手动拼接 SQL 字符串(仅限数据源完全可信的情况,并且必须有严格的防 SQL 注入措施)。
几个容易被忽略的细节:
* 尽量避免在循环里反复调用 `preparedStatement.setString(i, null)`,null 值优先使用 `setNull(i, Types.VARCHAR)`。
* 字段的 Ja va 类型最好和数据库定义的类型严格匹配。比如数据库字段是 `TINYINT`,就别用 `setInt()` 去强转,会增加类型转换开销。
* 使用 MySQL 8.0+ 驱动时,开启 `cachePrepStmts=true&prepStmtCacheSize=250` 可以复用 `PreparedStatement` 对象,减少 SQL 解析的重复劳动。
实际压测中,同样是插入 10 万条数据,**开启 `rewriteBatchedStatements` 前后的耗时可能相差 5 到 10 倍**;而事务粒度从“每条 commit”调到“每 5000 条 commit”,又能再砍掉 30% 到 60% 的时间。这些参数组合的效果不是线性的,上线前务必用真实数据集跑一遍压力测试,才能找到最适合你业务的配置。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8