Производительность пакетной вставки JDBC - PullRequest
60 голосов
/ 08 июня 2010

Мне нужно вставить пару сотен миллионов записей в базу данных mysql. Я пакетно вставляю это 1 миллион за один раз. Пожалуйста, смотрите мой код ниже. Это кажется медленным. Есть ли способ оптимизировать его?

try {
        // Disable auto-commit
        connection.setAutoCommit(false);

        // Create a prepared statement
        String sql = "INSERT INTO mytable (xxx), VALUES(?)";
        PreparedStatement pstmt = connection.prepareStatement(sql);

        Object[] vals=set.toArray();
        for (int i=0; i<vals.length; i++) {
            pstmt.setString(1, vals[i].toString());
            pstmt.addBatch();
        }

        // Execute the batch
        int [] updateCounts = pstmt.executeBatch();
        System.out.append("inserted "+updateCounts.length);

Ответы [ 6 ]

160 голосов
/ 16 мая 2012

У меня была похожая проблема с производительностью в mysql, и я решил ее, задав свойства useServerPrepStmts и rewriteBatchedStatements в URL-адресе соединения.

Connection c = DriverManager.getConnection("jdbc:mysql://host:3306/db?useServerPrepStmts=false&rewriteBatchedStatements=true", "username", "password");
53 голосов
/ 04 июня 2014

Я хотел бы подробнее остановиться на ответе Бертиля, поскольку я экспериментировал с параметрами URL-адреса соединения.

rewriteBatchedStatements=true является важным параметром. useServerPrepStmts по умолчанию уже имеет значение false, и даже изменение его на true не имеет большого значения с точки зрения производительности пакетной вставки.

Теперь я думаю, что пришло время написать, как rewriteBatchedStatements=true значительно повышает производительность. Это делает rewriting of prepared statements for INSERT into multi-value inserts when executeBatch() ( Источник ). Это означает, что вместо отправки следующих n INSERT операторов на сервер MySQL каждый раз, когда вызывается executeBatch():

INSERT INTO X VALUES (A1,B1,C1)
INSERT INTO X VALUES (A2,B2,C2)
...
INSERT INTO X VALUES (An,Bn,Cn)

Будет отправлен один оператор INSERT:

INSERT INTO X VALUES (A1,B1,C1),(A2,B2,C2),...,(An,Bn,Cn)

Вы можете наблюдать это, переключаясь на запись в журнал mysql (по SET global general_log = 1), которая будет регистрировать в файле каждый оператор, отправляемый на сервер mysql.

11 голосов
/ 08 июня 2010

Вы можете вставлять несколько строк одним оператором вставки, делая несколько тысяч за один раз, что может значительно ускорить процесс, то есть вместо выполнения, например. 3 вставки в форме INSERT INTO tbl_name (a,b,c) VALUES(1,2,3);, вы делаете INSERT INTO tbl_name (a,b,c) VALUES(1,2,3),(1,2,3),(1,2,3); (Возможно, JDBC .addBatch () выполняет аналогичную оптимизацию сейчас - хотя mysql addBatch раньше был полностью неоптимизирован и в любом случае просто выдавал отдельные запросы - я не делаю знать, если это все еще имеет место с последними драйверами)

Если вам действительно нужна скорость, загрузите данные из файла, разделенного запятыми, с помощью LOAD DATA INFILE , мы увеличим скорость примерно в 7-8 раз по сравнению с десятками миллионов вставок.

4 голосов
/ 08 июня 2010

Если:

  1. Это новая таблица, или сумма для вставки больше, чем уже вставленные данные
  2. В таблице есть индексы
  3. Вам не нужен другой доступ к таблице во время вставки

Тогда ALTER TABLE tbl_name DISABLE KEYS может значительно повысить скорость ваших вставок. Когда вы закончите, запустите ALTER TABLE tbl_name ENABLE KEYS, чтобы начать строить индексы, что может занять некоторое время, но не так долго, как это делается для каждой вставки.

1 голос
/ 26 августа 2015
try {
        // Disable auto-commit
        connection.setAutoCommit(false);
        int maxInsertBatch = 10000;     
        // Create a prepared statement
        String sql = "INSERT INTO mytable (xxx), VALUES(?)";
        PreparedStatement pstmt = connection.prepareStatement(sql);

        Object[] vals=set.toArray();
        int count = 1;
        for (int i=0; i<vals.length; i++) {
            pstmt.setString(1, vals[i].toString());
            pstmt.addBatch();
            if(count%maxInsertBatch == 0){
                 pstmt.executeBatch();
            }
            count++;
        }

        // Execute the batch
        pstmt.executeBatch();
        System.out.append("inserted "+count);
1 голос
/ 08 июня 2010

Вы можете попробовать использовать объект DDBulkLoad.

// Get a DDBulkLoad object
DDBulkLoad bulkLoad = DDBulkLoadFactory.getInstance(connection);
bulkLoad.setTableName(“mytable”);
bulkLoad.load(“data.csv”);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...