Как ускорить работу в PostgreSQL

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

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

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

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

Ответ 1

См. заполнить базу данных в руководстве 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 для быстрого тестирования.

Ответ 2

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

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

Ответ 3

В дополнение к превосходному сообщению в блоге 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, выпуская серию неподготовленных вставных операторов, так что для достижения самой быстрой вставки вам нужно вручную выполнить указанные выше шаги.

Ответ 4

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