Как ускорить производительность вставки в PostgreSQL

131

Я тестирую производительность вставки Postgres. У меня есть таблица с одним столбцом с номером в качестве своего типа данных. На нем есть индекс. Я заполнил базу данных, используя этот запрос:

insert into aNumber (id) values (564),(43536),(34560) ...

Я вставил 4 миллиона строк очень быстро 10 000 за один раз с запросом выше. После того, как база данных достигла 6 миллионов строк, производительность резко сократилась до 1 миллиона строк каждые 15 минут. Есть ли какой-либо трюк для повышения производительности вставки? Мне нужна оптимальная производительность в этом проекте.

Использование Windows 7 Pro на компьютере с оперативной памятью 5 ГБ.

  • 5
    Стоит упомянуть и вашу версию Pg в вопросах. В этом случае это не имеет большого значения, но для многих вопросов.
  • 1
    сбросьте индексы в таблице и сработает, если таковые имеются, и запустите скрипт вставки. После завершения массовой загрузки вы можете воссоздать индексы.
Теги:
sql-insert
bulkinsert

4 ответа

362
Лучший ответ

См. заполнить базу данных в руководстве PostgreSQL, depesz excellent-as -общая статья по этой теме и этот вопрос SO.

(Обратите внимание, что этот ответ относится к загрузке данных в существующую БД или к созданию нового. Если вам интересна производительность восстановления БД с помощью pg_restore или psql выполнения вывода pg_dump, большая часть это не действует, поскольку pg_dump и pg_restore уже выполняют такие действия, как создание триггеров и индексов после завершения восстановления схемы + данных).

Там многое предстоит сделать. Идеальным решением было бы импортировать в таблицу UNLOGGED без индексов, затем изменить ее на регистрацию и добавить индексы. К сожалению, в PostgreSQL 9.4 нет поддержки для изменения таблиц из UNLOGGED для входа в систему. 9.5 добавляет ALTER TABLE ... SET LOGGED, чтобы вы могли это сделать.

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

В противном случае:

  • Отключить любые триггеры в таблице

  • Отбросьте индексы перед запуском импорта, заново создайте их. (Для создания индекса за один проход требуется гораздо меньше времени, чем для добавления к нему одинаковых данных, и полученный индекс намного более компактен).

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

  • Если возможно, используйте COPY вместо INSERT s

  • Если вы не можете использовать COPY, рассмотрите возможность использования многозначного INSERT, если это возможно. Вы, кажется, делаете это уже. Не пытайтесь перечислить слишком много значений в одном VALUES; эти значения должны располагаться в памяти несколько раз, поэтому держите их в несколько сотен за утверждение.

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

  • Используйте synchronous_commit=off и огромный commit_delay для уменьшения затрат fsync(). Это не поможет, если вы потратили свою работу на крупные транзакции.

  • INSERT или COPY параллельно с несколькими соединениями. Сколько зависит от вашей дисковой подсистемы; как правило, вы хотите, чтобы одно подключение на физический жесткий диск использовалось при использовании прямого прикрепленного хранилища.

  • Задайте высокое значение checkpoint_segments и включите log_checkpoints. Посмотрите журналы журналов PostgreSQL и убедитесь, что они не жалуются на часто встречающиеся контрольные точки.

  • Если вам не нравится потерять весь ваш кластер PostgreSQL (ваша база данных и все остальные в одном кластере) до катастрофического повреждения, если система во время импорта выйдет из строя, вы можете остановить Pg, установить fsync=off, запустите Pg, сделайте свой импорт, затем (жизненно) остановите Pg и снова установите fsync=on. См. Конфигурация WAL. Не делайте этого, если в любой базе данных вашей установки PostgreSQL уже есть какие-либо данные. Если вы установите fsync=off, вы также можете установить full_page_writes=off; снова, просто не забудьте включить его после импорта, чтобы предотвратить повреждение базы данных и потерю данных. См. несрочные настройки в руководстве Pg.

Вы также должны посмотреть на настройку вашей системы:

  • Используйте SSD хорошего качества для хранения как можно больше. Хорошие SSD-накопители с надежными защищенными от электропитания кэшами с обратной записью делают транзакции невероятно быстрыми. Они менее полезны, когда вы следуете приведенному выше совету - что уменьшает количество дисков/количество fsync(), но все равно может быть большой помощью. Не используйте дешевые SSD без надлежащей защиты от сбоя питания, если вы не заботитесь о сохранении ваших данных.

  • Если вы используете RAID 5 или RAID 6 для прямого прикрепленного хранилища, остановитесь сейчас. Верните данные, перестройте свой RAID-массив на RAID 10 и повторите попытку. RAID 5/6 безнадежно для производительности навальной записи - хотя хороший RAID-контроллер с большим кешем может помочь.

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

  • Если возможно, храните WAL (pg_xlog) на отдельном диске/диске. Там мало смысла использовать отдельную файловую систему на том же диске. Люди часто предпочитают использовать пару RAID1 для WAL. Опять же, это оказывает большее влияние на системы с высокими коэффициентами фиксации, и это малоэффективно, если вы используете таблицу с разметкой в ​​качестве цели загрузки данных.

Вы также можете быть заинтересованы в Оптимизировать PostgreSQL для быстрого тестирования.

  • 1
    Согласитесь ли вы, что штраф за запись в RAID 5/6 несколько снизится, если использовать твердотельные накопители хорошего качества? Очевидно, что все еще есть штраф, но я думаю, что разница гораздо менее болезненна, чем с жесткими дисками.
  • 1
    Я не проверял это. Я бы сказал, что это, вероятно, не так уж и плохо - неприятные эффекты усиления записи и (для небольших записей) потребность в цикле чтения-изменения-записи все еще существуют, но серьезное наказание за чрезмерный поиск не должно возникать.
Показать ещё 18 комментариев
8

Использовать COPY table TO ... WITH BINARY, который соответствует документации, несколько быстрее, чем текстовые и CSV-форматы." Только делайте это, если у вас есть миллионы строк для вставки, и если вам удобны двоичные данные.

Вот пример рецепта на Python, используя psycopg2 с двоичным вводом.

  • 1
    Бинарный режим может сэкономить много времени на некоторых входах, таких как временные метки, где их анализ нетривиален. Для многих типов данных это не дает большой выгоды или может даже быть немного медленнее из-за увеличенной пропускной способности (например, маленькие целые числа). Хороший вопрос, поднимающий это.
6

В дополнение к превосходному сообщению в блоге Craig Ringer и depesz, если вы хотите ускорить вставку через ODBC (psqlodbc) интерфейс используя вставки готовой инструкции внутри транзакции, вам нужно сделать несколько дополнительных действий, чтобы сделать ее быстрой:

  • Установите уровень отката на ошибки в "Транзакция", указав Protocol=-1 в строке подключения. По умолчанию psqlodbc использует уровень "Statement", который создает SAVEPOINT для каждого оператора, а не для всей транзакции, что делает вставки медленнее.
  • Используйте подготовленные на стороне сервера инструкции, указав UseServerSidePrepare=1 в строке подключения. Без этой опции клиент отправляет весь оператор insert вместе с каждой вставленной строкой.
  • Отключить автоматическую фиксацию для каждого оператора с помощью SQLSetConnectAttr(conn, SQL_ATTR_AUTOCOMMIT, reinterpret_cast<SQLPOINTER>(SQL_AUTOCOMMIT_OFF), 0);
  • После того, как все строки были вставлены, выполните транзакцию с помощью SQLEndTran(SQL_HANDLE_DBC, conn, SQL_COMMIT);. Нет необходимости явно открывать транзакцию.

К сожалению, psqlodbc "реализует" SQLBulkOperations, выпуская серию неподготовленных вставных операторов, так что для достижения самой быстрой вставки вам нужно вручную выполнить указанные выше шаги.

0

Для оптимальной производительности вставки отключите индекс, если это опция для вас. Кроме того, лучше использовать аппаратное обеспечение (диск, память)

Ещё вопросы

Сообщество Overcoder
Наверх
Меню