Одновременные команды базы данных (PostgreSQL) в одной транзакции - PullRequest
4 голосов
/ 01 ноября 2011

Я пишу приложение .NET 4, которое импортирует большой объем данных из файла в базу данных PostgreSQL 9.1.Профилирование показывает, что вызовы БД для фактической вставки данных занимают более 90% времени.Сервер БД, по-видимому, привязан к ЦП и использует все один ЦП.

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

Можно ли как-то посылать параллельные команды на сервер БД (чтобы использовать все его ЦП), но при этом гарантировать, что либо весь импорт завершится успешноили никаких изменений не сделано?Насколько я понимаю, транзакция не может быть использована из нескольких потоков для одновременного выполнения нескольких команд?Я использую Npgsql в качестве поставщика ADO.NET, если это имеет значение.

Ответы [ 4 ]

4 голосов
/ 01 ноября 2011

Транзакция не может обрабатываться параллельно несколькими потоками со стандартным PostgreSQL до Postgres 9,6 , где эта функция была добавлена ​​как "параллельный запрос" .

Однако кажется подозрительным, что ваша операция INSERT связана с процессором. Несколько вещей могут быть улучшены здесь. Как именно вы отправляете данные на сервер? Есть в основном четыре способа INSERT данных в таблицу:

  1. по одной строке за раз с выражением VALUES, обеспечивающим литералы
  2. несколько строк одновременно VALUES выражение
  3. INSERT с SELECT (вставка 0-n строк)
  4. COPY

COPY - самый быстрый метод на сегодняшний день.

  • Это быстрее удалить индексы перед массовым INSERT / COPY и воссоздать их впоследствии. Постепенное добавление кортежей индекса гораздо менее эффективно, чем создание индекса сразу.

  • Триггеры, ограничения или ограничения внешнего ключа - это другие факторы, которые могут замедлить вас. Может быть, вы могли бы отключить / удалить до массовой загрузки, а затем включить / восстановить?

Существует также ряд настроек, которые могут существенно изменить ситуацию.

  • Вы можете отключить fsync и synchronous_commit . (Рискованный!)

  • Отключить autovacuum временно. Выполните ANALYZE сразу после этого. (Осторожней!)

Прочтите статью о Массовая загрузка и восстановление и Настройка сервера PostgreSQL в Вики Postgres, особенно параграфы checkpoint_segments и checkpoint_completion_target .

Операция может быть не такой ограниченной, как кажется. Посмотрите на этот абзац в PostgreSQL Wiki .

Еще одним источником замедления может быть регистрация. Например, log_statement = all создает огромные файлы журналов по цене, особенно с однострочными вставками.

Вот быстрый способ проверить все ваши пользовательские настройки в PostgreSQL Wiki еще раз.

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

CREATE TEMP TABLE x_tmp AS SELECT * FROM real_tbl LIMIT 0;

Подумайте, как работать с последовательностями и другими значениями по умолчанию! INSERT все данные в промежуточные таблицы, затем записать в целевые таблицы одной командой. Индексы и ограничения снова отключаются, но на гораздо более короткое время.

INSERT INTO real_tbl SELECT * FROM x_tmp ORDER BY something;
DROP TABLE x_tmp;

Может быть значительно быстрее. Обязательно используйте достаточно оперативной памяти для различных настроек. В частности, обратите внимание на temp_buffers.

0 голосов
/ 01 ноября 2011

Обратите внимание, что pg_restore теперь использует режим с несколькими потоками, чтобы получить преимущества от многоядерной архитектуры в восстановлении дампов сжатия.Так что управление тяжелым импортом по нескольким направлениям, безусловно, хорошая идея.Я видел сообщения о хороших достижениях при установке количества заданий на 2 * число ядер для pg_restore.

Но pg_restore не может использовать --single-transaction с этим параметром.Так что такая же проблема, как и у вас.Вы можете попробовать 2-фазные транзакции фиксации с помощью операторов PREPARE TRANSACTION , это обычно делается менеджерами транзакций, а не приложениями, но это может помочь вам сделать недействительными несколько транзакций, если в одной из них произойдет сбойВаш процесс импорта.

0 голосов
/ 01 ноября 2011

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

Сначала вы должны установить max_prepared_transactions в значение выше 0 и перезапустить postgresql. После этого вы начинаете транзакцию в одном сеансе следующим образом:

begin; 
select yada; 
insert yada; 
update yada; 
prepare transaction 'mytrans';

В этот момент вы получите уведомление «ПОДГОТОВИТЬ СДЕЛКУ» или «ОТДЕЛКА». Если вы получили откат от любой из ваших подготовленных транзакций, то вы можете выполнить откат, подготовленный для каждой из ваших транзакций, и ни одна из них не будет выполнена. Обратите внимание, что вы не должны оставлять множество готовых транзакций.

0 голосов
/ 01 ноября 2011

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

  • Определить новую, ненормализованную таблицу, содержащую ваши входные данные.Давайте назовем это «подготовка»
  • Заполните эту таблицу, используя несколько потоков и несколько соединений.
  • Как только данные будут там, вставьте необходимые идентификаторы, используя последовательность из real столы в промежуточный стол.
  • На этом параллельная рабочая фаза заканчивается.
  • Используйте одну транзакцию и выполните соответствующие операторы массовой вставки, перемещая ваши данные из промежуточной таблицы в реальные таблицы.
  • Сокращение промежуточной таблицы.

В этом сценарии ваше текущее узкое место (ограничение ЦП из-за обработки ввода) устраняется за счет выплаты большего количества монет ввода-вывода.

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