Оптимизация MySQL INSERT с множеством ЗНАЧЕНИЙ (), (), (); - PullRequest
0 голосов
/ 02 мая 2018

Я пытаюсь улучшить производительность своего Java-приложения, и в данный момент я концентрируюсь на одном конечном пункте, который должен вставить большой объем данных в mysql.

Я использую простой JDBC с драйвером клиента MariaDB Java:

try (PreparedStatement stmt = connection.prepareStatement(
            "INSERT INTO data (" +
                    "fId, valueDate, value, modifiedDate" +
                    ") VALUES (?,?,?,?)") {
    for (DataPoint dp : datapoints) {
        stmt.setLong(1, fId);
        stmt.setDate(2, new java.sql.Date(dp.getDate().getTime()));
        stmt.setDouble(3, dp.getValue());
        stmt.setDate(4, new java.sql.Date(modifiedDate.getTime()));
        stmt.addBatch();
    }        
    int[] results = statement.executeBatch();
}

Из заполнения новой БД из выгруженных файлов я знаю, что max_allowed_packet важен, и у меня установлено значение 536 870 912 байт.

В https://dev.mysql.com/doc/refman/5.7/en/insert-optimization.html говорится, что:

Если вы вставляете много строк из одного и того же клиента одновременно, используйте операторы INSERT с несколькими списками VALUES, чтобы вставить несколько строки одновременно. Это значительно быстрее (во многих случаях быстрее случаи), чем использование отдельных однорядных операторов INSERT. Если ты добавив данные в непустую таблицу, вы можете настроить переменная bulk_insert_buffer_size , чтобы сделать вставку данных еще быстрее. См. Раздел 5.1.7, «Системные переменные сервера» .

На моих БД установлено 8 МБ

Я также читал о key_buffer_size (в настоящее время установлено значение 16 МБ).

Я обеспокоен тем, что этих двух последних может быть недостаточно. Я могу сделать некоторые грубые вычисления на входе JSON для этого алгоритма, потому что он выглядит примерно так:

[{"actualizationDate":null,"data":[{"date":"1999-12-31","value":0},
{"date":"2000-01-07","value":0},{"date":"2000-01-14","value":3144},
{"date":"2000-01-21","value":358},{"date":"2000-01-28","value":1049},
{"date":"2000-02-04","value":-231},{"date":"2000-02-11","value":-2367},
{"date":"2000-02-18","value":-2651},{"date":"2000-02-25","value":-
393},{"date":"2000-03-03","value":1725},{"date":"2000-03-10","value":-
896},{"date":"2000-03-17","value":2210},{"date":"2000-03-24","value":1782},

и похоже, что 8МБ, настроенный для bulk_insert_buffer_size, может быть легко превышен, если не key_buffer_size.

Но в документации по MySQL упоминаются только таблицы MyISAM движка, и в настоящее время я использую InnoDB таблицы.

Я могу настроить некоторые тесты, но было бы хорошо знать, как это сломается или ухудшится, если вообще.

[ПРАВИТЬ] У меня есть --rewriteBatchedStatements=true. На самом деле вот моя строка подключения:

jdbc:p6spy:mysql://myhost.com:3306/mydb\
    ?verifyServerCertificate=true\
    &useSSL=true\
    &requireSSL=true\
    &cachePrepStmts=true\
    &cacheResultSetMetadata=true\
    &cacheServerConfiguration=true\
    &elideSetAutoCommits=true\
    &maintainTimeStats=false\
    &prepStmtCacheSize=250\
    &prepStmtCacheSqlLimit=2048\
    &rewriteBatchedStatements=true\
    &useLocalSessionState=true\
    &useLocalTransactionState=true\
    &useServerPrepStmts=true

(из https://github.com/brettwooldridge/HikariCP/wiki/MySQL-Configuration)

1 Ответ

0 голосов
/ 02 мая 2018

Альтернативой является выполнение партии время от времени. Это позволяет уменьшить размер пакетов и сосредоточиться на более важных проблемах.

int batchSize = 0;

for (DataPoint dp : datapoints) {
    stmt.setLong(1, fId);
    stmt.setDate(2, new java.sql.Date(dp.getDate().getTime()));
    stmt.setDouble(3, dp.getValue());
    stmt.setDate(4, new java.sql.Date(modifiedDate.getTime()));
    stmt.addBatch();

    //When limit reach, execute and reset the counter
    if(batchSize++ >= BATCH_LIMIT){
        statement.executeBatch();

        batchSize = 0;
    }
}        

// To execute the remaining items
if(batchSize > 0){
    statement.executeBatch();
}

Я обычно использую константу или параметр, основанный на реализации DAO, чтобы быть более динамичным, но партия из 10_000 строк - хорошее начало.

private static final int BATCH_LIMIT = 10_000;

Обратите внимание, что в этом нет необходимости очищать пакет после выполнения. Даже если это не указано в документации Statement.executeBatch, это в спецификации JDBC 4.3

14 Пакетных обновлений
14.1 Описание пакетных обновлений
14.1.2. Успешное выполнение

Вызов метода executeBatch закрывает текущий набор результатов вызывающего объекта Statement, если он открыт.
Пакет оператора сбрасывается на пустой после возврата executeBatch.

Управление результатом немного сложнее, но вы все равно можете объединить результаты, если они вам нужны. Это можно проанализировать в любое время, так как ResultSet больше не нужен.

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