我需要使用JDBC在Oracle-DB中进行大量插入,即两位数百万插入。为此,我使用的是受Efficient way to do batch INSERTS with JDBC启发的以下类:
public class Inserter {
private final int batchSize;
private final Connection con; // with .setAutoCommit(false)
private final PreparedStatement ps;
private int currentSize = 0;
public Inserter(Connection con, PreparedStatement ps, int batchSize) {
this.con = con;
this.ps = ps;
this.batchSize = batchSize;
}
public void addInsert(Object[] vals) throws SQLException {
ps.clearParameters(); // should be redundant, but better safe than sorry
for (int i = 0; i < val.length; i++) {
this.ps.setObject(i + 1, vals[i]);
}
ps.addBatch();
currentSize++;
if (currentSize >= batchSize) {
ps.executeBatch();
currentSize = 0;
}
}
public void flush() {/** to flush leftovers */}
}
虽然这种插入方式可以正常工作,但速度非常慢。 JDBC batch insert performance描述了如何从根本上解决MySQL的确切问题,因为rewriteBatchedStatements
在Oracle上似乎并不存在,在这里并没有太大帮助。
为了提高性能,我还尝试根据Oracle 11g - most efficient way of inserting multiple rows将Statement切换为一个大的INSERT ALL ...
/ INSERT APPEND ...
语句,这会使一切变得更慢。
因此,我的问题是,除了简单地使用addBatch()
和executeBatch()
之外,还有没有其他方法可以优化这些插入内容?还是上面的Inserter
类中可能存在一些严重的,效率低下的错误?任何帮助将不胜感激。
更多可能有用的信息:
要插入的表已分区,每个分区大约有一到一千万行。
在表上有一个唯一的约束,看起来像unique(id1, id2, id3, id4)
,其中所有列均为NUMBER
类型,并且进一步由外键约束绑定到其他表中的主键。
编辑:
根据评论的建议,我将setObject(index, val)
呼叫切换为:
setInt(index, val)
,setFloat(index, val)
,...
,setNull(index, type)
调用在适当的地方
setObject(index, val, type)
和setNull(index, typR)
两个版本均未显着提高性能。
此外,我尝试将数据插入没有任何约束的登台表中,这也没有带来更好的性能。
相比之下,将数据导出到CSV并使用SQL * Loader加载将导致性能显着提高,例如,对于最慢的表 1 ,每秒〜4.5k =>〜50k +行。
这使我相信JDBC的瓶颈所在。
1 A,在我的特殊情况下,使用SQL * Loader并不是(期望的)选项。
答案 0 :(得分:0)
您从不提交批处理。如果可能,在executeBatch之后添加提交 如果未提交查询,则将创建大型回滚段,这可能会减慢速度 关闭数据库。还要删除ps.clearParameters(),因为您始终会覆盖所有参数或不覆盖任何参数。最好使用专业版的setter代替setObject