Java 使用JDBC执行批插入的有效方法

Java 使用JDBC执行批插入的有效方法,java,sql,performance,jdbc,Java,Sql,Performance,Jdbc,在我的应用程序中,我需要做很多插入。这是一个Java应用程序,我使用纯JDBC来执行查询。数据库是Oracle。不过,我已经启用了批处理,因此它为我节省了执行查询的网络延迟。但是查询作为单独的插入连续执行: insert into some_table (col1, col2) values (val1, val2) insert into some_table (col1, col2) values (val3, val4) insert into some_table (col1, col2

在我的应用程序中,我需要做很多插入。这是一个Java应用程序,我使用纯JDBC来执行查询。数据库是Oracle。不过,我已经启用了批处理,因此它为我节省了执行查询的网络延迟。但是查询作为单独的插入连续执行:

insert into some_table (col1, col2) values (val1, val2)
insert into some_table (col1, col2) values (val3, val4)
insert into some_table (col1, col2) values (val5, val6)
我想知道以下插入形式是否更有效:

insert into some_table (col1, col2) values (val1, val2), (val3, val4), (val5, val6)
i、 e.将多个插件折叠为一个插件


让批量插入更快的其他技巧是什么?

显然,您必须进行基准测试,但是如果您使用PreparedStatement而不是Statement,那么在JDBC上发出多个插入将快得多。

语句提供了以下选项:

Statement stmt = con.createStatement();

stmt.addBatch("INSERT INTO employees VALUES (1000, 'Joe Jones')");
stmt.addBatch("INSERT INTO departments VALUES (260, 'Shoe')");
stmt.addBatch("INSERT INTO emp_dept VALUES (1000, 260)");

// submit a batch of update commands for execution
int[] updateCounts = stmt.executeBatch();

这是前面两个答案的混合:

  PreparedStatement ps = c.prepareStatement("INSERT INTO employees VALUES (?, ?)");

  ps.setString(1, "John");
  ps.setString(2,"Doe");
  ps.addBatch();

  ps.clearParameters();
  ps.setString(1, "Dave");
  ps.setString(2,"Smith");
  ps.addBatch();

  ps.clearParameters();
  int[] results = ps.executeBatch();

使用INSERTALL语句怎么样

INSERT ALL

INTO table_name VALUES ()

INTO table_name VALUES ()

...

SELECT Statement;
我记得最后一个select语句是必需的,以使此请求成功。不记得为什么了。 你也可以考虑使用<强> PraveReald> <强>。很多优点


如果迭代次数少,Farid使用PreparedStatements将比使用PreparedStatements慢得多。为了从使用PrepareStatement而不是语句中获得性能优势,您需要在迭代次数至少为50次或更高的循环中使用它。

您可以在java中使用addBatch和executeBatch进行批插入参见示例:

尽管问题要求使用JDBC高效地插入到Oracle中,我目前正在使用DB2(在IBM大型机上),从概念上讲,插入将是类似的,因此我认为在这两者之间查看我的度量可能会有所帮助

  • 一次插入一条记录

  • 插入一批记录(非常高效)

这就是指标

1)一次插入一条记录 第一个事务大约需要
120-150ms
,这是为了执行,随后的事务大约只需要
50ms
。(仍然很高,但我的数据库位于不同的服务器上(我需要对网络进行故障排除))

2)通过批插入(有效的一个)-通过
preparedStatement.executeBatch()实现
1000笔交易

total time taken to insert the batch = 341 ms
因此,在
~5000ms
中进行100个事务(一次一个trxn)减少到
~150ms
(一批100个记录)


注意-忽略我的网络速度非常慢,但度量值是相对的。

在我的代码中,我无法直接访问“preparedStatement”,因此我无法使用batch,我只向它传递查询和参数列表。然而,诀窍是创建一个可变长度的insert语句和一个参数的LinkedList。效果与上面的示例相同,参数输入长度可变。请参见下文(省略错误检查)。 假设“myTable”有3个可更新字段:f1、f2和f3

String []args={"A","B","C", "X","Y","Z" }; // etc, input list of triplets
final String QUERY="INSERT INTO [myTable] (f1,f2,f3) values ";
LinkedList params=new LinkedList();
String comma="";
StringBuilder q=QUERY;
for(int nl=0; nl< args.length; nl+=3 ) { // args is a list of triplets values
    params.add(args[nl]);
    params.add(args[nl+1]);
    params.add(args[nl+2]);
    q.append(comma+"(?,?,?)");
    comma=",";
}      
int nr=insertIntoDB(q, params);
String[]args={“A”、“B”、“C”、“X”、“Y”、“Z”};//等,输入三胞胎列表
final String QUERY=“插入[myTable](f1、f2、f3)值”;
LinkedList params=新建LinkedList();
字符串逗号=”;
StringBuilder q=查询;
对于(int-nl=0;nl
在我的DBInterface类中,我有:

int insertIntoDB(String query, LinkedList <String>params) {
    preparedUPDStmt = connectionSQL.prepareStatement(query);
    int n=1;
    for(String x:params) {
        preparedUPDStmt.setString(n++, x);
    }
    int updates=preparedUPDStmt.executeUpdate();
    return updates;
}
int insertIntoDB(字符串查询,LinkedList参数){
PreparedUpdsmt=connectionSQL.prepareStatement(查询);
int n=1;
用于(字符串x:params){
准备更新stmt.setString(n++,x);
}
int updates=preparedUpdsmt.executeUpdate();
返回更新;
}

您可以使用此
rewriteBatchedStatements
参数使批插入更快


您可以在这里阅读有关参数的信息:

SQLite:以上答案都是正确的。对于SQLite,它有点不同。没有什么真正有帮助的,即使将其放入批处理中(有时)也不能提高性能。在这种情况下,请尝试在完成后禁用自动提交和手动提交(警告!当多个连接同时写入时,可能会与这些操作冲突)


这是完美的解决方案,因为语句只准备(解析)一次在这种情况下是不必要的。请确保测量它。根据JDBC驱动程序的实现,这可能是预期的每批一次往返,但也可能最终是每语句一次往返。prepareStatement/setXXX-这是应该的方式!对于mysql,还可以在url中添加以下内容:“&UseServerPrepsmts=false&rewriteBatchedStatements=true”,虽然最终结果相同,但在该方法中,多个语句被解析,这对于批量来说要慢得多,实际上比单独执行每个语句效率不高。此外,请尽可能使用PreparedStatement进行重复查询,因为它们的性能要好得多。@AshishPatil:您有没有任何基准测试,可以使用PreparedStatement进行测试,也可以不使用PreparedStatement?哇!8年后。尽管如此,@prayagupd在他的回答中给出了详细的统计数据,这是最近才给出的。非常感谢你这么做。这在动态插入数据时非常有用,而且您没有时间检查参数的数据类型。不,永远不会。普通语句(不是PrepareStatement)对象必须完成PreparedStatement所做的所有事情,事实上它是PreparedStatement的包装器,实际上它也完成了准备的部分。两者之间的区别在于,语句对象以静默方式准备语句,并在每次执行时验证它,而作为准备好的语句,语句只执行一次,然后可以多次执行以处理批处理中的每个项。这个答案是否有效?哇!在插入SQL Server时,我测试了您的“将多个插入折叠为一个”,从107行/秒增加到3333行/秒
total time taken to insert the batch = 127 ms
total time taken to insert the batch = 341 ms
String []args={"A","B","C", "X","Y","Z" }; // etc, input list of triplets
final String QUERY="INSERT INTO [myTable] (f1,f2,f3) values ";
LinkedList params=new LinkedList();
String comma="";
StringBuilder q=QUERY;
for(int nl=0; nl< args.length; nl+=3 ) { // args is a list of triplets values
    params.add(args[nl]);
    params.add(args[nl+1]);
    params.add(args[nl+2]);
    q.append(comma+"(?,?,?)");
    comma=",";
}      
int nr=insertIntoDB(q, params);
int insertIntoDB(String query, LinkedList <String>params) {
    preparedUPDStmt = connectionSQL.prepareStatement(query);
    int n=1;
    for(String x:params) {
        preparedUPDStmt.setString(n++, x);
    }
    int updates=preparedUPDStmt.executeUpdate();
    return updates;
}
// connect(), yourList and compiledQuery you have to implement/define beforehand
try (Connection conn = connect()) {
     conn.setAutoCommit(false);
     preparedStatement pstmt = conn.prepareStatement(compiledQuery);
     for(Object o : yourList){
        pstmt.setString(o.toString());
        pstmt.executeUpdate();
        pstmt.getGeneratedKeys(); //if you need the generated keys
     }
     pstmt.close();
     conn.commit();

}