我试图在Java中用大约50,000行10列填充resultSet
然后使用batchExecute
的{{1}}方法将它们插入到另一个表中。
为了让这个过程更快,我做了一些研究,发现在将数据读入resultSet时,fetchSize起着重要的作用。
fetchSize非常低可能导致到服务器的次数过多,而fetchSize非常高可能会阻塞网络资源,因此我尝试了一点并设置了适合我的基础设施的最佳大小。
我正在读取此resultSet并创建插入语句以插入到另一个数据库的另一个表中。
像这样的东西(只是一个样本,而不是真正的代码):
PreparedStatement
批量更新(50,000行10个字符串)时,使用可更新的for (i=0 ; i<=50000 ; i++) {
statement.setString(1, "a@a.com");
statement.setLong(2, 1);
statement.addBatch();
}
statement.executeBatch();
或PreparedStaement批量执行是否更好?
答案 0 :(得分:45)
我将依次解决您的问题。
这可能因每个JDBC驱动程序而异,但我研究的少数几个将遍历每个批处理条目,并将每个参数与准备好的语句句柄一起发送到数据库以供执行。也就是说,在上面的示例中,将使用50,000对参数执行50,000个预处理语句,但这些50,000个步骤可以在较低级别的“内循环”中完成,这是节省时间的地方。相当拉伸的类比,就像从“用户模式”退出到“内核模式”并在那里运行整个执行循环。您可以为每个批次条目节省潜入和退出低级别模式的费用。
您通过在Statement#executeBatch()
执行批处理之前推送50,000个参数集来隐式定义它。批量大小为1也同样有效。
考虑在批量插入之前显式打开事务,然后提交它。不要让数据库或JDBC驱动程序在批处理中的每个插入步骤周围强加事务边界。您可以使用Connection#setAutoCommit(boolean)
方法控制JDBC层。首先从自动提交模式中取出连接,然后填充批次,启动事务,执行批处理,然后通过Connection#commit()
提交事务。
此建议假定您的插入不会与并发编写者竞争,并假定这些事务边界将为您提供从源表中读取的足够一致的值,以便在插入中使用。如果情况并非如此,那么赞成正确而不是速度。
ResultSet
或PreparedStatement
是否更好? 没有什么能比你选择的JDBC驱动程序更好,但我希望后者 - PreparedStatement
和Statement#executeBatch()
能在这里胜出。语句句柄可以具有关联的列表或“批处理参数”数组,每个条目是在调用Statement#executeBatch()
和Statement#addBatch()
(或Statement#clearBatch()
)之间提供的参数集。每次调用addBatch()
时,列表都会增长,在您致电executeBatch()
之前不会刷新。因此,Statement
实例实际上充当了参数缓冲区;你是为了方便而交易内存(使用Statement
实例代替你自己的外部参数集缓冲区。)
同样,只要我们不讨论特定的 JDBC驱动程序,您应该认为这些答案是通用的和推测性的。每个驱动程序的复杂程度各不相同,每个驱动程序的优化程度各不相同。
答案 1 :(得分:14)
批次将以“一次性完成”完成 - 这就是你要求它做的。
在一次通话中尝试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行不应超过几秒钟。
答案 2 :(得分:1)
如果只是数据库中一个/多个表中的数据要插入此表而没有干预(对结果集的更改),则调用statement.executeUpdate(SQL)
执行{{3由于没有开销,因此速度更快。没有数据流出数据库,整个操作都在数据库上而不是应用程序中。
答案 3 :(得分:0)
批量未记录更新不会为您提供所需的改进性能。见this