Является ли подготовленный оператор значительно медленнее, чем одиночный запрос для большого количества вставок?

Я вставляю несколько миллионов строк в таблицу MySql. Я использую подготовленный оператор, как показано ниже.

Ожидается ли, что создание одной строки вставки, подобной приведенной ниже, будет значительно быстрее?

Подход с одной строкой из Is 22 секунды — хорошее время для вставки 500 строк в mysql? :

INSERT INTO example
  (example_id, name, value, other_value)
VALUES
  (100, 'Name 1', 'Value 1', 'Other 1'),
  (101, 'Name 2', 'Value 2', 'Other 2'),
  (102, 'Name 3', 'Value 3', 'Other 3'),
  (103, 'Name 4', 'Value 4', 'Other 4');

Чем я сейчас занимаюсь:

// 
// method to do upload
// 

public static void doUpload(Connection conn) {
    log.info("Deleting existing data...");
    Database.update("truncate table attribute", conn);
    log.info("Doing inserts");
    String sqlString = "insert into attribute values (null,?,?,?)";
    int max = 1000000;
    PreparedStatement ps = Database.getPreparedStatement(sqlString, conn);
    for (int i = 0; i < max; i++) {
        // add params
        String subjectId = i+"";
        addParam(subjectId, "GENDER", getGender(), ps);
        addParam(subjectId, "AGE", getAge(), ps);
        addParam(subjectId, "CITY", getCity(), ps);
        addParam(subjectId, "FAVORITE_COLOR", getColor(), ps);
        addParam(subjectId, "PET", getPet(), ps);
        if (i % 1000 == 0) {
            log.info("Executing " + i + " of " + max);
            Database.execute(ps);
            log.info("Done with batch update");
            ps = Database.getPreparedStatement(sqlString, conn);
        }
    }
    if (Database.isClosed(ps) == false) {
        Database.execute(ps);
    }
}

// 
// method to add param to the prepared statement
// 

private static void addParam(String subjectId, String name, String val, PreparedStatement ps) {
    ArrayList<String> params;
    params = new ArrayList<String>();
    params.add(subjectId + "");
    params.add(name);
    params.add(val);
    Database.addToBatch(params, ps);
}

// 
// addToBatch
// 

public static void addToBatch(List<String> params, PreparedStatement ps) {
    try {
        for (int i = 0; i < params.size(); i++) {
            ps.setString((i + 1), params.get(i));
        }
        ps.addBatch();
    } catch (Exception exp) {
        throw new RuntimeException(exp);
    }
}

Каков самый быстрый способ сделать этот тип вставки?

В настоящее время я вставляю 1000 строк примерно за 5 секунд. Разумно ли ожидать гораздо большего, чем это? Я работаю локально и уже удалил все индексы в таблице, в которую вставляю.


person John    schedule 15.08.2020    source источник
comment
Это зависит от контекста. Если вставка миллионов строк за раз — это то, что ваше приложение будет делать очень часто. Чем, возможно, выигрыш в производительности по сравнению с выигрышем в безопасности того стоит. Если нет, я бы сказал, иди с подготовленным заявлением.   -  person Nicolas    schedule 16.08.2020
comment
Вам может понравиться моя презентация Быстрая загрузка данных!   -  person Bill Karwin    schedule 16.08.2020
comment
Вы делаете это неправильно. Вы должны вызывать executeBatch() и повторное использование файла PreparedStatement. Это, безусловно, быстрее, чем самостоятельно создавать строки SQL, и более экономично в отношении памяти.   -  person user207421    schedule 16.08.2020
comment
Спасибо @BillKarwin. Теперь я получаю около 25 тыс. строк в секунду, используя Java ps и rewriteBatchedStatements=true, чего на данный момент достаточно. Я забыл о функции загрузки файла MySql, но я не думаю, что мне нужно идти в этом направлении на данный момент. Спасибо за прекрасную статью, обязательно скину ссылку.   -  person John    schedule 17.08.2020


Ответы (3)


Самый быстрый способ выполнить пакетную вставку с помощью JDBC — использовать addBatch/executeBatch, что вы, похоже, уже делаете.

Пример кода см.

Но это только даст вам столько производительности. Для реального повышения производительности добавьте rewriteBatchedStatements=true к URL-адресу JDBC. Вы увидите значительное улучшение.

См. MySQL и JDBC с rewriteBatchedStatements=true.

Имейте в виду, что то, что вы предлагаете в своем подходе с одной строкой, похоже, но rewriteBatchedStatements=true также делает сетевое взаимодействие с базой данных более эффективным.

person GreyBeardedGeek    schedule 16.08.2020
comment
Да, добавление rewriteBatchedStatements=true к URL-адресу соединения сработало волшебным образом. То, что заняло около 5 секунд, теперь занимает около 0,1 секунды. - person John; 16.08.2020

Не уверен, что делает Database.getPreparedStatement, но обычно вам не нужно воссоздавать объект PreparedStatement после каждого пакетного выполнения, вы все равно можете использовать его повторно. Также вы пытались установить больший размер партии? На данный момент размер вашей партии составляет 1000, вы пытались увеличить его?

person Ivan    schedule 16.08.2020

Подготовленные заявления предлагают преимущество безопасности. Теоретически подготовленный оператор предварительно скомпилирован и должен обеспечивать более высокую производительность.

person Blacksmith    schedule 15.08.2020