Какой самый быстрый способ сделать массовую вставку в Postgres?

Мне нужно программно вставить 10 миллионов записей в базу данных postgres. В настоящее время я выполняю 1000 операторов вставки в одном "запросе".

Есть ли лучший способ сделать это, какой-то объемный оператор вставки, о котором я не знаю?

Ответ 1

PostgreSQL имеет руководство о том, как наилучшим образом заполнить базу данных изначально, и они предлагают использовать COPY для массовых загрузочных строк. В руководстве есть несколько других полезных советов о том, как ускорить процесс, например, удаление индексов и внешних ключей перед загрузкой данных (и их последующее добавление).

Ответ 2

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

INSERT INTO films (code, title, did, date_prod, kind) VALUES
    ('B6717', 'Tampopo', 110, '1985-02-10', 'Comedy'),
    ('HG120', 'The Dinner Game', 140, DEFAULT, 'Comedy');

Вышеупомянутый код вставляет две строки, но вы можете продлить его произвольно, пока не нажмете максимальное количество подготовленных токенов операторов (это может быть $999, но я не уверен на 100%). Иногда нельзя использовать COPY, и это достойная замена для этих ситуаций.

Ответ 3

Один из способов ускорить работу - это явно выполнить несколько вложений или скопировать транзакцию (скажем 1000). По умолчанию поведение Postgres заключается в фиксации после каждого утверждения, поэтому путем пакетной фиксации вы можете избежать некоторых накладных расходов. Как говорит гид в ответе Даниэля, вам может потребоваться отключить аутокоммит для этого. Также обратите внимание на комментарий внизу, который предлагает увеличить размер wal_buffers до 16 МБ, также может помочь.

Ответ 4

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

Мой первый подход всегда: создать таблицу (temp) со структурой, подобной целевой таблице (создать таблицу tmp AS select * from target, где 1 = 0), и начать с чтения файла в таблицу temp. Затем я проверяю, что можно проверить: дубликаты, ключи, которые уже существуют в целевом объекте и т.д.

Затем я просто делаю "вставить в целевой выбор" из tmp или аналогичный.

Если это не удается или занимает слишком много времени, я отменяю его и рассматриваю другие методы (временно снижая индексы/ограничения и т.д.)

Ответ 6

UNNEST функция с массивами может использоваться вместе с синтаксисом multival VALUES. Я думаю, что этот метод медленнее, чем с использованием COPY, но он мне полезен при работе с psycopg и python (python list, переданный в cursor.execute, становится pg ARRAY):

INSERT INTO tablename (fieldname1, fieldname2, fieldname3)
VALUES (
    UNNEST(ARRAY[1, 2, 3]), 
    UNNEST(ARRAY[100, 200, 300]), 
    UNNEST(ARRAY['a', 'b', 'c'])
);

без VALUES с использованием подзаголовка с дополнительной проверкой на наличие:

INSERT INTO tablename (fieldname1, fieldname2, fieldname3)
SELECT * FROM (
    SELECT UNNEST(ARRAY[1, 2, 3]), 
           UNNEST(ARRAY[100, 200, 300]), 
           UNNEST(ARRAY['a', 'b', 'c'])
) AS temptable
WHERE NOT EXISTS (
    SELECT 1 FROM tablename tt
    WHERE tt.fieldname1=temptable.fieldname1
);

тот же синтаксис для массовых обновлений:

UPDATE tablename
SET fieldname1=temptable.data
FROM (
    SELECT UNNEST(ARRAY[1,2]) AS id,
           UNNEST(ARRAY['a', 'b']) AS data
) AS temptable
WHERE tablename.id=temptable.id;

Ответ 7

Я очень быстро выполнил загрузку данных Postgresq с помощью собственных методов libpq. Попробуйте мой пакет https://www.nuget.org/packages/NpgsqlBulkCopy/

Ответ 8

Я только что столкнулся с этой проблемой и рекомендовал csvsql для массового импорта в Postgres. Чтобы выполнить массовую вставку, вы просто createdb, а затем используйте csvsql, который подключается к вашей базе данных и создает отдельные таблицы для всей папки CSV.

$ createdb test 
$ csvsql --db postgresql:///test --insert examples/*.csv