IT Share you

JDBC로 일괄 INSERTS를 수행하는 효율적인 방법

shareyou 2020. 12. 10. 21:30
반응형

JDBC로 일괄 INSERTS를 수행하는 효율적인 방법


내 앱에서 INSERTS를 많이해야합니다. Java 앱이고 일반 JDBC를 사용하여 쿼리를 실행하고 있습니다. DB는 Oracle입니다. 그래도 일괄 처리를 활성화했기 때문에 쿼리를 실행하는 데 필요한 네트워크 대기 시간이 절약됩니다. 그러나 쿼리는 별도의 INSERT로 직렬로 실행됩니다.

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가 더 효율적인지 궁금합니다.

insert into some_table (col1, col2) values (val1, val2), (val3, val4), (val5, val6)

즉, 여러 INSERT를 하나로 축소합니다.

일괄 INSERT를 더 빠르게 만들기위한 다른 팁이 있습니까?


이것은 이전 두 가지 답변을 혼합 한 것입니다.

  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();

질문은 JDBC를 사용하여 Oracle에 효율적으로 삽입하는 것을 요구하지만 현재 DB2 (IBM 메인 프레임에서)를 사용하고 있습니다. 개념적으로 삽입하는 것이 비슷하므로 다음 사이의 메트릭을 보는 것이 도움이 될 것이라고 생각했습니다.

  • 한 번에 하나의 레코드 삽입

  • 레코드 배치 삽입 (매우 효율적)

여기에 통계가 있습니다.

1) 한 번에 하나의 레코드 삽입

public void writeWithCompileQuery(int records) {
    PreparedStatement statement;

    try {
        Connection connection = getDatabaseConnection();
        connection.setAutoCommit(true);

        String compiledQuery = "INSERT INTO TESTDB.EMPLOYEE(EMPNO, EMPNM, DEPT, RANK, USERNAME)" +
                " VALUES" + "(?, ?, ?, ?, ?)";
        statement = connection.prepareStatement(compiledQuery);

        long start = System.currentTimeMillis();

        for(int index = 1; index < records; index++) {
            statement.setInt(1, index);
            statement.setString(2, "emp number-"+index);
            statement.setInt(3, index);
            statement.setInt(4, index);
            statement.setString(5, "username");

            long startInternal = System.currentTimeMillis();
            statement.executeUpdate();
            System.out.println("each transaction time taken = " + (System.currentTimeMillis() - startInternal) + " ms");
        }

        long end = System.currentTimeMillis();
        System.out.println("total time taken = " + (end - start) + " ms");
        System.out.println("avg total time taken = " + (end - start)/ records + " ms");

        statement.close();
        connection.close();

    } catch (SQLException ex) {
        System.err.println("SQLException information");
        while (ex != null) {
            System.err.println("Error msg: " + ex.getMessage());
            ex = ex.getNextException();
        }
    }
}

100 건의 거래에 대한 측정 항목 :

each transaction time taken = 123 ms
each transaction time taken = 53 ms
each transaction time taken = 48 ms
each transaction time taken = 48 ms
each transaction time taken = 49 ms
each transaction time taken = 49 ms
...
..
.
each transaction time taken = 49 ms
each transaction time taken = 49 ms
total time taken = 4935 ms
avg total time taken = 49 ms

첫 번째 트랜잭션이 약 복용 120-150ms에 대한 어떤 쿼리 구문 분석 , 후속 트랜잭션이 단지 약 복용 후 실행 50ms. (아직 높지만 내 데이터베이스가 다른 서버에 있습니다 (네트워크 문제를 해결해야 함))

2) 배치에 삽입 (효율적) -preparedStatement.executeBatch()

public int[] writeInABatchWithCompiledQuery(int records) {
    PreparedStatement preparedStatement;

    try {
        Connection connection = getDatabaseConnection();
        connection.setAutoCommit(true);

        String compiledQuery = "INSERT INTO TESTDB.EMPLOYEE(EMPNO, EMPNM, DEPT, RANK, USERNAME)" +
                " VALUES" + "(?, ?, ?, ?, ?)";
        preparedStatement = connection.prepareStatement(compiledQuery);

        for(int index = 1; index <= records; index++) {
            preparedStatement.setInt(1, index);
            preparedStatement.setString(2, "empo number-"+index);
            preparedStatement.setInt(3, index+100);
            preparedStatement.setInt(4, index+200);
            preparedStatement.setString(5, "usernames");
            preparedStatement.addBatch();
        }

        long start = System.currentTimeMillis();
        int[] inserted = preparedStatement.executeBatch();
        long end = System.currentTimeMillis();

        System.out.println("total time taken to insert the batch = " + (end - start) + " ms");
        System.out.println("total time taken = " + (end - start)/records + " s");

        preparedStatement.close();
        connection.close();

        return inserted;

    } catch (SQLException ex) {
        System.err.println("SQLException information");
        while (ex != null) {
            System.err.println("Error msg: " + ex.getMessage());
            ex = ex.getNextException();
        }
        throw new RuntimeException("Error");
    }
}

100 개의 트랜잭션 배치에 대한 메트릭은 다음과 같습니다.

total time taken to insert the batch = 127 ms

1000 건의 거래

total time taken to insert the batch = 341 ms

따라서 ~5000ms(한 번에 하나의 trxn으로) 100 개의 트랜잭션을 만드는 것이 ~150ms(100 개의 레코드 배치로 ) 감소합니다 .

참고-매우 느린 내 네트워크는 무시하지만 메트릭 값은 상대적입니다.


Statement당신에게 다음과 같은 옵션을 제공합니다 :

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();

You'll have to benchmark, obviously, but over JDBC issuing multiple inserts will be much faster if you use a PreparedStatement rather than a Statement.


You can use this rewriteBatchedStatements parameter to make the batch insert even faster.

you can read here about the param: MySQL and JDBC with rewriteBatchedStatements=true


How about using the INSERT ALL statement ?

INSERT ALL

INTO table_name VALUES ()

INTO table_name VALUES ()

...

SELECT Statement;

I remember that the last select statement is mandatory in order to make this request succeed. Don't remember why though. You might consider using PreparedStatement instead as well. lots of advantages !

Farid


You can use addBatch and executeBatch for batch insert in java See the Example : Batch Insert In Java


In my code I have no direct access to the 'preparedStatement' so I cannot use batch, I just pass it the query and a list of parameters. The trick however is to create a variable length insert statement, and a LinkedList of parameters. The effect is the same as the top example, with variable parameter input length.See below (error checking omitted). Assuming 'myTable' has 3 updatable fields: f1, f2 and 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);

in my DBInterface class I have:

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;
}

Using PreparedStatements will be MUCH slower than Statements if you have low iterations. To gain a performance benefit from using a PrepareStatement over a statement, you need to be using it in a loop where iterations are at least 50 or higher.


Batch insert using statement

int a= 100;
            try {
                        for (int i = 0; i < 10; i++) {
                            String insert = "insert into usermaster"
                                    + "("
                                    + "userid"
                                    + ")"
                                    + "values("
                                    + "'" + a + "'"
                                    + ");";
                            statement.addBatch(insert);
                            System.out.println(insert);
                            a++;
                        }
                      dbConnection.commit();
                    } catch (SQLException e) {
                        System.out.println(" Insert Failed");
                        System.out.println(e.getMessage());
                    } finally {

                        if (statement != null) {
                            statement.close();
                        }
                        if (dbConnection != null) {
                            dbConnection.close();
                        }
                    }

참고URL : https://stackoverflow.com/questions/3784197/efficient-way-to-do-batch-inserts-with-jdbc

반응형