Эффективный способ сделать пакетные вкладыши с JDBC - PullRequest
54 голосов
/ 24 сентября 2010

В моем приложении мне нужно сделать много вставок.Это Java-приложение, и я использую простой JDBC для выполнения запросов.БД является 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 в один.*

Какие-нибудь еще советы по ускорению пакетной вставки?

Ответы [ 10 ]

112 голосов
/ 24 сентября 2010

Это смесь двух предыдущих ответов:

  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();
20 голосов
/ 13 марта 2017

Хотя вопрос задает эффективную вставку в Oracle с использованием JDBC , в настоящее время я играю с 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

Итак, выполнение 100 транзакций за ~5000ms (с одним trxn за раз) уменьшается до ~150ms (с пакетом из 100 записей).

ПРИМЕЧАНИЕ. - Игнорируйте мою сеть, которая работает очень медленно, но значения метрик будут относительными.

6 голосов
/ 24 сентября 2010

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();
4 голосов
/ 24 сентября 2010

Очевидно, что вам придется тестировать, но по сравнению с JDBC выпуск нескольких вставок будет намного быстрее, если вы используете PreparedStatement, а не Statement.

1 голос
/ 30 марта 2019

Вы можете использовать этот параметр rewriteBatchedStatements, чтобы сделать пакетную вставку еще быстрее.

Вы можете прочитать здесь о параметре: MySQL и JDBC с rewriteBatchedStatements = true

0 голосов
/ 14 ноября 2018

В моем коде у меня нет прямого доступа к «readyStatement», поэтому я не могу использовать пакет, я просто передаю ему запрос и список параметров. Однако хитрость заключается в создании оператора вставки переменной длины и списка параметров 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);

в моем классе 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;
}
0 голосов
/ 18 сентября 2013

Вы можете использовать addBatch и executeBatch для пакетной вставки в Java. См. Пример: Пакетная вставка в Java

0 голосов
/ 12 июня 2013

Пакетная вставка с использованием выписки

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();
                        }
                    }
0 голосов
/ 30 сентября 2010

Использование PreparedStatements будет НАМНОГО медленнее, чем Statements, если у вас низкие итерации.Чтобы получить выигрыш в производительности от использования PrepareStatement над оператором, вы должны использовать его в цикле, где количество итераций не менее 50 или выше.

0 голосов
/ 24 сентября 2010

Как насчет использования оператора INSERT ALL?

INSERT ALL

INTO table_name VALUES ()

INTO table_name VALUES ()

...

SELECT Statement;

Я помню, что последний оператор выбора является обязательным для успешного выполнения этого запроса.Хотя не помню почему.Вы также можете использовать PreparedStatement .много преимуществ!

Фарид

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...