Мне нужно программно вставить 10 миллионов записей в базу данных postgres. В настоящее время я выполняю 1000 операторов вставки в одном "запросе".
Есть ли лучший способ сделать это, какой-то объемный оператор вставки, о котором я не знаю?
Мне нужно программно вставить 10 миллионов записей в базу данных postgres. В настоящее время я выполняю 1000 операторов вставки в одном "запросе".
Есть ли лучший способ сделать это, какой-то объемный оператор вставки, о котором я не знаю?
PostgreSQL имеет руководство о том, как наилучшим образом заполнить базу данных изначально, и они предлагают использовать COPY для массовых загрузочных строк. В руководстве есть несколько других полезных советов о том, как ускорить процесс, например, удаление индексов и внешних ключей перед загрузкой данных (и их последующее добавление).
Существует альтернатива использованию 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, и это достойная замена для этих ситуаций.
Один из способов ускорить работу - это явно выполнить несколько вложений или скопировать транзакцию (скажем 1000). По умолчанию поведение Postgres заключается в фиксации после каждого утверждения, поэтому путем пакетной фиксации вы можете избежать некоторых накладных расходов. Как говорит гид в ответе Даниэля, вам может потребоваться отключить аутокоммит для этого. Также обратите внимание на комментарий внизу, который предлагает увеличить размер wal_buffers до 16 МБ, также может помочь.
В основном это зависит от (другой) активности в базе данных. Подобные операции эффективно блокируют всю базу данных для других сеансов. Другим соображением является датамодель и наличие ограничений, триггеров и т.д.
Мой первый подход всегда: создать таблицу (temp) со структурой, подобной целевой таблице (создать таблицу tmp AS select * from target, где 1 = 0), и начать с чтения файла в таблицу temp. Затем я проверяю, что можно проверить: дубликаты, ключи, которые уже существуют в целевом объекте и т.д.
Затем я просто делаю "вставить в целевой выбор" из tmp или аналогичный.
Если это не удается или занимает слишком много времени, я отменяю его и рассматриваю другие методы (временно снижая индексы/ограничения и т.д.)
Вы можете использовать COPY table TO ... WITH BINARY
, который " несколько быстрее, чем текстовые и CSV-форматы." Только делайте это, если у вас есть миллионы строк для вставки, и если вам удобны двоичные данные.
Вот пример рецепта на Python, используя psycopg2 с двоичным вводом.
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;
Я очень быстро выполнил загрузку данных Postgresq с помощью собственных методов libpq. Попробуйте мой пакет https://www.nuget.org/packages/NpgsqlBulkCopy/
Я только что столкнулся с этой проблемой и рекомендовал csvsql для массового импорта в Postgres. Чтобы выполнить массовую вставку, вы просто createdb
, а затем используйте csvsql
, который подключается к вашей базе данных и создает отдельные таблицы для всей папки CSV.
$ createdb test
$ csvsql --db postgresql:///test --insert examples/*.csv