使用预准备语句批量更新在 Java 中批量插入

我正在尝试用大约50,000行(每行10列)填充Java中的结果集,然后使用的方法将它们插入到另一个表中。batchExecutePreparedStatement

为了使这个过程更快,我做了一些研究,发现在将数据读取到resultSet中时,fetchSize起着重要作用。

具有非常低的 fetchSize 可能会导致到服务器的行程过多,而非常高的 fetchSize 可能会阻塞网络资源,因此我进行了一些实验,并设置了适合我的基础结构的最佳大小。

我正在读取此结果集并创建插入语句以插入到不同数据库的另一个表中。

像这样的东西(只是一个示例,而不是真正的代码):

for (i=0 ; i<=50000 ; i++) {
    statement.setString(1, "a@a.com");
    statement.setLong(2, 1);
    statement.addBatch();
}
statement.executeBatch();
  • executeBatch 方法会尝试一次发送所有数据吗?
  • 有没有办法定义批大小?
  • 有没有更好的方法来加快批量插入的过程?

批量更新(50,000 行 10 列)时,使用可更新的还是具有批处理执行的预处理状态更好?ResultSet


答案 1

我将依次回答您的问题。

  • executeBatch 方法会尝试一次发送所有数据吗?

这可能因每个 JDBC 驱动程序而异,但我研究过的少数几个驱动程序将迭代每个批处理条目,并每次将参数与预准备的语句句柄一起发送到数据库以执行。也就是说,在上面的示例中,预准备语句将执行 50,000 次,其中包含 50,000 对参数,但这 50,000 个步骤可以在较低级别的“内部循环”中完成,这是节省时间的地方。作为一个相当延伸的类比,这就像从“用户模式”下降到“内核模式”,并在那里运行整个执行循环。您可以节省每个批处理条目的潜入和退出较低级别模式的成本。

  • 有没有办法定义批大小

您在此处隐式定义了它,方法是在通过 执行批处理之前推送 50,000 个参数集。批大小为 1 同样有效。Statement#executeBatch()

  • 有没有更好的方法来加快批量插入的过程?

请考虑在批处理插入之前显式打开事务,并在之后提交事务。不要让数据库或 JDBC 驱动程序在批处理中的每个插入步骤周围强加事务边界。您可以使用 Connection#setAutoCommit(boolean) 方法控制 JDBC 层。首先将连接从自动提交模式中移除,然后填充批处理,启动事务,执行批处理,然后通过 Connection#commit() 提交事务。

此建议假定您的插入不会与并发写入器争用,并假定这些事务边界将为您提供从源表中读取的足够一致的值,以便在插入中使用。如果不是这样,请偏爱正确性而不是速度。

  • 是将可更新的结果集还是准备语句与批处理执行一起使用更好?

没有什么比使用您选择的JDBC驱动程序进行测试更好的了,但我期望后者 - 并且会在这里获胜。语句句柄可能具有关联的“批处理参数”列表或数组,其中每个条目都是在调用 和 (或 ) 之间提供的参数集。每次调用 时,该列表都会增长,并且在调用 之前不会刷新。因此,该实例实际上充当参数缓冲区;您正在为了方便而交易内存(使用实例代替您自己的外部参数集缓冲区)。PreparedStatementStatement#executeBatch()Statement#executeBatch()Statement#addBatch()Statement#clearBatch()addBatch()executeBatch()StatementStatement

同样,只要我们不讨论特定的JDBC驱动程序,您就应该考虑这些答案的一般和推测。每个驱动程序的复杂程度各不相同,每个驱动程序在追求的优化方面也会有所不同。


答案 2

批处理将“一次全部”完成 - 这就是您要求它执行的操作。

50,000似乎有点大,试图在一个电话中。我会把它分成1000个较小的块,就像这样:

final int BATCH_SIZE = 1000;
for (int i = 0; i < DATA_SIZE; i++) {
  statement.setString(1, "a@a.com");
  statement.setLong(2, 1);
  statement.addBatch();
  if (i % BATCH_SIZE == BATCH_SIZE - 1)
    statement.executeBatch();
}
if (DATA_SIZE % BATCH_SIZE != 0)
  statement.executeBatch();

50,000 行不应超过几秒钟。


推荐