Подтвердить что ты не робот

Массовая вставка в Java с использованием подготовленных операторов пакетного обновления

Я пытаюсь заполнить resultSet в Java примерно 50 000 строк из 10 столбцов а затем вставляя их в другую таблицу, используя метод batchExecute PreparedStatement.

Чтобы ускорить процесс, я провел некоторое исследование и обнаружил, что при чтении данных в resultSet важна роль fetchSize.

Наличие очень низкого fetchSize может привести к слишком большому количеству поездок на сервер, и очень высокий fetchSize может блокировать сетевые ресурсы, поэтому я немного экспериментировал и настраивал оптимальный размер, соответствующий моей инфраструктуре.

Я читаю этот resultSet и создаю инструкции insert для вставки в другую таблицу другой базы данных.

Что-то вроде этого (просто образец, а не настоящий код):

for (i=0 ; i<=50000 ; i++) {
    statement.setString(1, "[email protected]");
    statement.setLong(2, 1);
    statement.addBatch();
}
statement.executeBatch();
  • Будет ли метод executeBatch попытаться сразу отправить все данные?
  • Есть ли способ определить размер партии?
  • Есть ли лучший способ ускорить процесс объемной вставки?

При обновлении навалом (50 000 строк 10 колоний) лучше ли использовать обновляемый ResultSet или PreparedStaement с пакетным исполнением?

4b9b3361

Ответ 1

Я буду решать ваши вопросы по очереди.

  • Будет ли метод executeBatch одновременно отправлять все данные?

Это может варьироваться в зависимости от каждого драйвера JDBC, но те немногие, которые я изучил, будут перебирать каждую запись пакета и отправлять аргументы вместе с подготовленным дескриптором инструкции каждый раз в базу данных для выполнения. То есть в вашем примере выше будет 50 000 исполнений подготовленного оператора с 50 000 пар аргументов, но эти 50 000 шагов могут быть выполнены в "внутреннем цикле" нижнего уровня, в котором происходит сбережение времени. довольно растянутая аналогия, это как отказ от "пользовательского режима" вниз в "режим ядра" и запуск всего цикла выполнения там. Вы сохраняете стоимость погружения в и из этого режима нижнего уровня для каждой партии.

  • Есть ли способ определить размер партии

Вы определили его неявно здесь, нажав 50 000 наборов аргументов перед выполнением пакета через Statement#executeBatch(). Размер партии один так же важен.

  • Есть ли лучший способ ускорить процесс объемной вставки?

Рассмотрите возможность открытия транзакции явно перед вставкой пакета и после этого завершите ее. Не позволяйте ни базе данных, ни драйверу JDBC накладывать границу транзакции на каждый шаг вставки в пакете. Вы можете управлять уровнем JDBC с помощью метода Connection#setAutoCommit(boolean). Сначала возьмите соединение из режима автоматической фиксации, затем заполните свои партии, запустите транзакцию, выполните пакет, затем совершите транзакцию через Connection#commit().

В этом совете предполагается, что ваши вставки не будут конкурировать с параллельными авторами и предполагают, что эти границы транзакций дадут вам достаточно согласованные значения, считанные из ваших исходных таблиц для использования в вставках. Если это не так, поддерживайте правильность по скорости.

  • Лучше ли использовать обновляемый ResultSet или PreparedStatement с пакетным исполнением?

Ничто не сравнится с тестированием вашего драйвера JDBC по выбору, но я ожидаю, что последние будут PreparedStatement и Statement#executeBatch(). У дескриптора оператора может быть связанный список или массив "пакетных аргументов", причем каждая запись представляет собой набор аргументов, предоставляемый между вызовами Statement#executeBatch() и Statement#addBatch() (или Statement#clearBatch()). Список будет расти с каждым вызовом addBatch(), а не сбрасываться до тех пор, пока вы не назовете executeBatch(). Следовательно, экземпляр Statement действительно действует как буфер аргументов; вы торгуете памятью для удобства (используя экземпляр Statement вместо вашего собственного внешнего буферного набора аргументов).

Опять же, вы должны учитывать эти ответы как общие, так и спекулятивные, пока мы не обсуждаем конкретный драйвер JDBC. Каждый драйвер отличается изощренностью, и каждый из них будет отличаться тем, какие оптимизации он преследует.

Ответ 2

Пакет будет выполняться в "все сразу" - это то, что вы просили сделать.

50 000 кажется немного большим, чтобы пытаться в один звонок. Я разбил бы его на меньшие куски 1000, например:

final int BATCH_SIZE = 1000;
for (int i = 0; i < DATA_SIZE; i++) {
  statement.setString(1, "[email protected]");
  statement.setLong(2, 1);
  statement.addBatch();
  if (i % BATCH_SIZE == BATCH_SIZE - 1)
    statement.executeBatch();
}
if (DATA_SIZE % BATCH_SIZE != 0)
  statement.executeBatch();

50 000 строк не должны занимать больше нескольких секунд.

Ответ 3

Если это просто данные из одной или нескольких таблиц в БД, которые нужно вставить в эту таблицу, и никаких вмешательств (изменений в набор результатов), вызовите statement.executeUpdate(SQL) для выполнения INSERT-SELECT, это быстрее, поскольку нет накладных расходов. Нет данных, выходящих за пределы БД, и вся операция находится в БД не в приложении.

Ответ 4

Массовое разблокированное обновление не даст вам улучшенной производительности, которую вы хотите, как вы это делаете. См. this