Массовая вставка в Java с использованием подготовленных операторов пакетного обновления - PullRequest
30 голосов
/ 01 августа 2011

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

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

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

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

Примерно так (просто пример, а не реальный код):

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

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

Ответы [ 4 ]

46 голосов
/ 01 августа 2011

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

  • Будет ли метод 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. Каждый драйвер отличается по сложности, и каждый будет отличаться в том, какие оптимизации он преследует.

14 голосов
/ 01 августа 2011

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

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

final int BATCH_SIZE = 1000;
for (int i = 0; i < DATA_SIZE; i++) {
  statement.setString(1, "a@a.com");
  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 строк не должны занимать более нескольких секунд.

1 голос
/ 01 августа 2011

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

0 голосов
/ 27 ноября 2015

Массовое незарегистрированное обновление не даст вам желаемой производительности, как вы хотите.Смотри это

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