JDBC批处理操作的理解

Pet*_*sky 5 java postgresql hibernate jdbc

我在我的应用程序中使用Hibernate ORM和PostgreSQL,有时我使用批处理操作.起初我不明白为什么在批处理大小为25的日志中,会生成25个查询,并且最初认为它无法正常工作.但之后我查看了pg驱动程序的源代码,并在PgStatement类中找到以下行:

 public int[] executeBatch() throws SQLException {
        this.checkClosed();
        this.closeForNextExecution();
        if (this.batchStatements != null && !this.batchStatements.isEmpty()) {
            this.transformQueriesAndParameters();
//confuses next line, because we have array of identical queries
            Query[] queries = (Query[])this.batchStatements.toArray(new Query[0]);
            ParameterList[] parameterLists = 
(ParameterList[])this.batchParameters.toArray(new ParameterList[0]); 
            this.batchStatements.clear();
            this.batchParameters.clear();
Run Code Online (Sandbox Code Playgroud)

并在PgPreparedStatement类中

    public void addBatch() throws SQLException {
        checkClosed();
        if (batchStatements == null) {
          batchStatements = new ArrayList<Query>();
          batchParameters = new ArrayList<ParameterList>();
        }

        batchParameters.add(preparedParameters.copy());
        Query query = preparedQuery.query;
    //confuses next line
        if (!(query instanceof BatchedQuery) || batchStatements.isEmpty()) {
          batchStatements.add(query);
        }
      }
Run Code Online (Sandbox Code Playgroud)

我注意到,如果批处理的大小为25,则会发送25个查询并附加参数.

数据库的日志确认了这一点,例如:

2017-12-06 01:22:08.023 MSK [18402] postgres@buzzfactory ?????????:  ?????????? S_3: BEGIN
2017-12-06 01:22:08.024 MSK [18402] postgres@buzzfactory ?????????:  ?????????? S_4: select nextval ('tests_id_seq')
2017-12-06 01:22:08.041 MSK [18402] postgres@buzzfactory ?????????:  ?????????? S_2: insert into tests (name, id) values ($1, $2)     
2017-12-06 01:22:08.041 MSK [18402] postgres@buzzfactory ???????????:  ?????????: $1 = 'test', $2 = '1'
2017-12-06 01:22:08.041 MSK [18402] postgres@buzzfactory ?????????:  ?????????? S_2: insert into tests (name, id) values ($1, $2)
2017-12-06 01:22:08.041 MSK [18402] postgres@buzzfactory ???????????:  ?????????: $1 = 'test', $2 = '2'
...
x23 queries with parameters 
...
2017-12-06 01:22:08.063 MSK [18402] postgres@buzzfactory ?????????:  ?????????? S_5: COMMIT
Run Code Online (Sandbox Code Playgroud)

但我认为必须使用25个参数的数组执行一个查询.或者我不明白批量插入如何与预准备语句一起使用?为什么要重复一次查询n次?

毕竟,我试图在这个地方调试我的查询

if (!(query instanceof BatchedQuery) || batchStatements.isEmpty()) {
Run Code Online (Sandbox Code Playgroud)

并注意到我的查询始终是SimpleQuery的实例而不是BatchedQuery.也许这是问题的解决方案?有关BatchedQuery的信息我找不到

Vla*_*kov 12

可能涉及各种类型的批处理,我将介绍PostgreSQL JDBC驱动程序(pgjdbc)的一部分.

TL; DR:在使用批处理API的情况下,pgjdbc确实使用较少的网络回合.BatchedQuery仅在reWriteBatchedInserts=true传递给pgjdbc连接设置时使用.

您可能会发现https://www.slideshare.net/VladimirSitnikv/postgresql-and-jdbc-striving-for-high-performance相关(幻灯片44,...)

在查询执行方面,网络延迟通常是经过时间的重要部分.

假设案例是插入10行.

  1. 没有批处理(例如只是PreparedStatement#execute在循环中).驱动程序将执行以下操作

    execute query
    sync <-- wait for the response from the DB
    execute query
    sync <-- wait for the response from the DB
    execute query
    sync <-- wait for the response from the DB
    ...
    
    Run Code Online (Sandbox Code Playgroud)

    值得注意的时间将花在"等待数据库"上

  2. JDBC批处理API.这PreparedStatement#addBatch()使得驱动程序能够在单个网络往返中发送多个"查询执行".然而,当前的实现仍然会将大批量分成较小的批次以避免TCP死锁.

    行动会好得多:

    execute query
    ...
    execute query
    execute query
    execute query
    sync <-- wait for the response from the DB
    
    Run Code Online (Sandbox Code Playgroud)
  3. 请注意,即使使用#addBatch"执行查询"命令也会产生开销.服务器需要花费大量时间来单独处理每条消息.

    减少查询数量的方法之一是使用多值插入.例如:

    insert into tab(a,b,c) values (?,?,?), (?,?,?), ..., (?,?,?)
    
    Run Code Online (Sandbox Code Playgroud)

    这个PostgreSQL允许一次插入多行.缺点是您没有详细的(每行)错误消息.目前,Hibernate没有实现多值插入.

    但是,自9.4.1209(2016-07-15)起,pgjdbc可以动态地将常规批量插入重写为多值.

    要激活多值重写,您需要添加reWriteBatchedInserts=true连接属性.该功能最初是在https://github.com/pgjdbc/pgjdbc/pull/491中开发的

    它足够聪明,可以使用2个语句来插入10行.第一个是8值语句,第二个是2值语句.使用2的幂使pgjdbc能够保持不同语句的数量合理,并且这可以提高性能,因为经常使用的语句是服务器准备的(请参阅PostgreSQL服务器端预处理语句的生命周期)

    BatchedQuery表示那种多值语句,因此您将看到reWriteBatchedInserts=true仅在大小写中使用的类.

    该功能的缺点可能包括:较低的细节作为"批处理结果".例如,常规批处理为您提供"per statement rowcount",但在多值情况下,您只需获得"语句已完成"状态.最重要的是,动态重写器可能无法解析某些SQL语句(例如https://github.com/pgjdbc/pgjdbc/issues/1045).