bigywater.tech
Java · 314

JDBC 批量更新:减少往返但控制批次大小

批量更新真的更快吗?批大小多少合适?MySQL 的 rewriteBatchedStatements 与 PostgreSQL 的 reWriteBatchedInserts 各有多大影响?一批里有一行出错时,已经成功的行算不算数?这篇用两个真实数据库逐项测。

932 → 60 ms
PostgreSQL 插入 20000 行:autocommit 逐行与批大小 100
649.9 → 67.5 ms
MySQL 批大小 1000:不加与加 rewriteBatchedStatements=true
[1, 1, -3, 1, 1]
MySQL 默认(continueBatchOnError=true)下批里第 3 行失败时的 getUpdateCounts

PostgreSQL 批大小

实测 · 本机执行输出
  逐行 executeUpdate,autocommit=true                 932.2 ms      21456 行/秒  表中 20000 行
  逐行 executeUpdate,autocommit=false,最后一次 commit     533.7 ms      37474 行/秒  表中 20000 行
  addBatch / executeBatch,批大小 1                    544.2 ms      36750 行/秒  表中 20000 行
  批大小 10                                           108.2 ms     184829 行/秒  表中 20000 行
  批大小 100                                           60.9 ms     328197 行/秒  表中 20000 行
  批大小 1000                                          59.5 ms     335964 行/秒  表中 20000 行
  批大小 10000                                         57.6 ms     347070 行/秒  表中 20000 行
  批大小 20000(一次发完)                                   55.8 ms     358600 行/秒  表中 20000 行

PostgreSQL 改写批量插入

实测 · 本机执行输出
  逐行 executeUpdate,autocommit=true                 924.4 ms      21635 行/秒  表中 20000 行
  逐行 executeUpdate,autocommit=false,最后一次 commit     532.4 ms      37567 行/秒  表中 20000 行
  addBatch / executeBatch,批大小 1                    544.6 ms      36722 行/秒  表中 20000 行
  批大小 10                                            87.9 ms     227591 行/秒  表中 20000 行
  批大小 100                                           31.5 ms     634043 行/秒  表中 20000 行
  批大小 1000                                          24.2 ms     824785 行/秒  表中 20000 行
  批大小 10000                                         25.9 ms     771029 行/秒  表中 20000 行
  批大小 20000(一次发完)                                   21.4 ms     933585 行/秒  表中 20000 行

MySQL 批大小

实测 · 本机执行输出
  逐行 executeUpdate,autocommit=true                1836.4 ms      10891 行/秒  表中 20000 行
  逐行 executeUpdate,autocommit=false,最后一次 commit     679.2 ms      29447 行/秒  表中 20000 行
  addBatch / executeBatch,批大小 1                   1306.5 ms      15308 行/秒  表中 20000 行
  批大小 10                                           718.0 ms      27855 行/秒  表中 20000 行
  批大小 100                                          648.8 ms      30828 行/秒  表中 20000 行
  批大小 1000                                         649.9 ms      30774 行/秒  表中 20000 行
  批大小 10000                                        646.2 ms      30948 行/秒  表中 20000 行
  批大小 20000(一次发完)                                  642.6 ms      31126 行/秒  表中 20000 行

MySQL 改写批量语句

实测 · 本机执行输出
  逐行 executeUpdate,autocommit=true                1839.7 ms      10871 行/秒  表中 20000 行
  逐行 executeUpdate,autocommit=false,最后一次 commit     691.0 ms      28945 行/秒  表中 20000 行
  addBatch / executeBatch,批大小 1                   1391.5 ms      14373 行/秒  表中 20000 行
  批大小 10                                           183.3 ms     109115 行/秒  表中 20000 行
  批大小 100                                           82.3 ms     243124 行/秒  表中 20000 行
  批大小 1000                                          67.5 ms     296087 行/秒  表中 20000 行
  批大小 10000                                         76.6 ms     261125 行/秒  表中 20000 行
  批大小 20000(一次发完)                                   78.7 ms     254213 行/秒  表中 20000 行

PostgreSQL 的失败语义

实测 · 本机执行输出
  批里第 3 行主键重复:autocommit=true,抛 BatchUpdateException;getUpdateCounts() 长度 5 = [-3, -3, -3, -3, -3];SQLState 23505
     回滚 / 失败后表里的 id:3
  批里第 3 行主键重复:autocommit=false,抛 BatchUpdateException;getUpdateCounts() 长度 5 = [-3, -3, -3, -3, -3];SQLState 23505
     回滚 / 失败后表里的 id:3
  成功的批:executeBatch() 返回 [1, 1](各行影响行数)

MySQL 的失败语义

实测 · 本机执行输出
  批里第 3 行主键重复:autocommit=true,抛 BatchUpdateException;getUpdateCounts() 长度 5 = [1, 1, -3, 1, 1];SQLState 23000
     回滚 / 失败后表里的 id:1,2,3,4,5
  批里第 3 行主键重复:autocommit=false,抛 BatchUpdateException;getUpdateCounts() 长度 5 = [1, 1, -3, 1, 1];SQLState 23000
     回滚 / 失败后表里的 id:3
  成功的批:executeBatch() 返回 [1, 1](各行影响行数)

MySQL continueBatchOnError=false

实测 · 本机执行输出
  批里第 3 行主键重复:autocommit=true,抛 BatchUpdateException;getUpdateCounts() 长度 2 = [1, 1];SQLState 23000
     回滚 / 失败后表里的 id:1,2,3
  批里第 3 行主键重复:autocommit=false,抛 BatchUpdateException;getUpdateCounts() 长度 2 = [1, 1];SQLState 23000
     回滚 / 失败后表里的 id:3
  成功的批:executeBatch() 返回 [1, 1](各行影响行数)

分步演示

JDBC 批量更新
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / OpenJDK(Temurin 17.0.20、Homebrew 21.0.12 与 27,每篇注明实际用的版本);用 javac 编译、java 直接运行,需要第三方库时用 Maven 解析依赖;计时用 System.nanoTime,数字每次运行会有小幅抖动。JDK 21.0.12;自己起的 PostgreSQL 16.15(fsync 开启,端口 9501)与 MySQL 26.7.0(端口 9601),都监听 127.0.0.1(用户自己的数据库实例没有被使用);pgjdbc 42.7.4、mysql-connector-j 9.1.0;每个配置只跑一次
    • 没有验证:跨机器网络(往返延迟更大时批处理的收益会更明显,本机回环几乎没有延迟)、批大小对内存的影响、语句缓存 / 服务端预编译(prepareThreshold、useServerPrepStmts)的作用、UPDATE / DELETE 批量、事务隔离或并发写入下的批量表现、COPY / LOAD DATA 等专用批量接口
    京ICP备2026067502号-1京公网安备11010502063487号