将 Java 的数千条记录插入到 Oracle,如何获得最佳性能?

Insert thousands of records from Java to Oracle, how can I get the best performance?

我需要在 java 中创建一个批处理过程,它将读取一个包含可变数量记录的文件(尽管可以安全地假设 5k+),处理它们,然后将记录插入到 oracle 11g 数据库中。我需要通过调用存储过程来完成。

几个月前我已经做了一个类似的过程,我对此并不太自豪(没有特别的原因,除了我确信它根本不是最佳的)。 之前的解决方案我将所有内容都保存在内存中,为了插入行,我创建了一个 INSERT ALL 语句,在该语句中我只是通过 stringbuilder 附加所有记录,然后执行该语句以一次性插入 15k+ 条记录。 这次我需要使用存储过程来插入数据。 我一直在阅读,现在知道有一些方法可以将数据数组发送到存储过程,所以我可以一次发送多条记录。

我是否应该创建一个存储过程来接收数组并在一次调用该 SP 时发送所有(可能是数千条)记录? 或者我应该将它限制为一次一定数量的记录并调用该 SP (records/limit) 次数?

或者我应该远离使用数组,并拥有一个只接收一条记录的信息的存储过程,并在有记录的情况下多次调用它?

如果我要进行多次调用,我正在考虑使用 PreparedStatements 以及 .addBatch() 和 .executeBatch() 方法,这是可行的方法吗?

我需要能够插入所有记录,并在出现任何错误时回滚。我将为此使用交易。从技术上讲,我不需要在性能方面达到任何门槛,但我对这个话题很感兴趣,现在可能是开始更多地担心它的好时机,所以我想要一些在这个话题上有经验的人的指示和技巧.

你应该使用存储过程(SP)吗?

我认为它不会有任何显着的性能改进,因为您有一个 INSERT 语句。如果您有一些复杂的查询并且使用它可以节省查询编译时间,那么 SP 将是有益的。一个简单的插入语句不会花费太多时间来编译。

因此,在我看来,使用 Java 的 PreparedStatement 即时发送您的查询。

我会遵循的方法:

由于您的要求是插入所有数据,出错时回滚,建议您批量插入整组数据。如果批处理失败,您可以回滚该批处理的更改并重试该批处理的插入。

如果您要一次发送整个数据集,那么您将不得不回滚由于单个插入语句中的错误而导致的整个更改。

使用批处理的另一个好处是您将在单个 JDBC 连接中发送批处理数据。建立、维护和清除连接会产生开销。因此,批处理在一定程度上节省了开销。

此外,您还可以使用多线程,您可以让任务读取一批数据、处理它们并插入它们(使用批处理)。您的任务可以从 HikariCP 等连接池访问 JDBC 个连接。因此,当一些线程忙于数据插入时,其他线程可以读取和处理数据。

关于多线程插入的一些 material 阅读:https://dba.stackexchange.com/questions/16131/how-does-oracle-handle-multiple-concurrent-inserts-against-one-table

PS: 欢迎建设性的批评

"Should I make a Stored procedure that receives arrays and just send all - potentially thousands - of the records in one single call to that SP? Or should I limit it to a certain amount of records at a time and call that SP (records/limit) amount of times?"

限制一定数量的记录。我通常从 100 到 1000 开始,具体取决于记录的总大小。

"Or should I stay away from using arrays, and have a stored procedure that just receives the information for one record, and call it as many times as I have records?"

没有。您将浪费 CPU,最重要的是:每次 Java 调用数据库时,发送消息和获取回复(与 "latency" 相关)都会花费时间。

"If I were to do multiple calls, I was thinking of utilizing PreparedStatements and the .addBatch() and .executeBatch() methods, would this be the way to go?"

是的,但是这些方法最适合使用 SQL 语句(例如 INSERT),而不是调用存储过程。

I need to be able to insert all the records, and rollback in case of any error.

关闭自动提交(我通常建议这样做)并在一切正常时提交。

如果你的存储过程没有任何附加值,只是简单地做插入,那么做批量插入会更简单高效。尽管有额外的复杂性,但使用存储过程有很好的论据,但是你必须填充数组。

在任何情况下,使用 绑定变量 并且不要将值连接到 SQL 语句(或调用 SP)作为文字是至关重要的。

此致,斯图阿什顿

P.S。对于 5k+ 记录,多线程是多余的。