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

我试图用Java填充一个带有大约50,000行10列的结果集,然后使用PreparedStatementbatchExecute方法将它们插入到另一个表中。

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

拥有一个非常低的fetchSize可能导致太多的服务器访问,并且非常高的fetchSize可以阻止网络资源,所以我尝试了一点,并设置了适合我的基础设施的最佳大小。

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

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

 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或PreparedStaement与批量执行是否更好?

我将依次解决您的问题。

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

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

  • 有没有办法定义批量大小

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

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

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

此建议假定您的插入不会与并发编写者竞争,并假定这些事务边界将为您提供从源表中读取的足够一致的值,以便在插入中使用。 如果情况并非如此,那么赞成正确性超过速度。

  • 使用可更新的ResultSetPreparedStatement与批处理执行是否更好?

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

同样,只要我们不讨论特定的 JDBC驱动程序,您应该将这些答案视为一般和推测。 每个驱动程序的复杂程度各不相同,每个驱动程序的优化程度各不相同。

批次将“全部一次”完成 – 这就是你要求它做的事情。

在一次通话中尝试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行不应超过几秒钟。

如果只是数据库中的一个/多个表中的数据要插入到此表中而没有干预(对结果集进行更改) ,则调用statement.executeUpdate(SQL)来执行INSERT-SELECT语句,这样更快,因为没有高架。 没有数据流出数据库,整个操作都在数据库上而不是应用程序中。

批量未记录更新不会为您提供所需的改进性能。 看到这个