Ответы:
В 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 МБ, также может помочь.
UNNEST
Функция с массивами может использоваться вместе с многорядным синтаксисом 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;
Вы можете использовать, COPY table TO ... WITH BINARY
что « несколько быстрее, чем текст и форматы CSV ». Делайте это только в том случае, если у вас есть миллионы строк для вставки и если вы знакомы с двоичными данными.
Вот пример рецепта в Python, использующего psycopg2 с двоичным вводом .
В основном это зависит от (другой) активности в базе данных. Такие операции эффективно замораживают всю базу данных для других сеансов. Другое соображение - это модель данных и наличие ограничений, триггеров и т. Д.
Мой первый подход всегда: создать (временную) таблицу со структурой, аналогичной целевой таблице (создать таблицу tmp AS select * from target, где 1 = 0), и начать с чтения файла во временную таблицу. Затем я проверяю, что можно проверить: дубликаты, ключи, которые уже существуют в цели, и т. Д.
Затем я просто делаю «сделать вставку в целевой select * from tmp» или подобное.
Если это не удается или занимает слишком много времени, я прекращаю его и рассматриваю другие методы (временное удаление индексов / ограничений и т. Д.)
Я реализовал очень быстрый загрузчик данных Postgresq с помощью собственных методов libpq. Попробуйте мой пакет https://www.nuget.org/packages/NpgsqlBulkCopy/
Я только что столкнулся с этой проблемой и рекомендую csvsql ( релизы ) для массового импорта в Postgres. Чтобы выполнить массовую вставку, вы просто createdb
и затем используете ее csvsql
, которая подключается к вашей базе данных и создает отдельные таблицы для всей папки CSV.
$ createdb test
$ csvsql --db postgresql:///test --insert examples/*.csv
Термин «объемные данные» относится к «большому количеству данных», поэтому естественно использовать исходные необработанные данные без необходимости их преобразования в SQL. Типичными файлами необработанных данных для «массовой вставки» являются форматы CSV и JSON .
В приложениях ETL и процессах приема данных нам нужно изменить данные перед их вставкой. Временная таблица потребляет (много) дискового пространства, и это не быстрый способ сделать это. Обертка внешних данных PostgreSQL (ИДП) является лучшим выбором.
CSV пример . Предположим , что tablename (x, y, z)
на SQL и CSV файл , как
fieldname1,fieldname2,fieldname3
etc,etc,etc
... million lines ...
Вы можете использовать классический SQL COPY
для загрузки ( как и исходные данные), в который tmp_tablename
они вставляют отфильтрованные данные tablename
... Но, чтобы избежать потребления диска, лучше всего проглотить непосредственно
INSERT INTO tablename (x, y, z)
SELECT f1(fieldname1), f2(fieldname2), f3(fieldname3) -- the transforms
FROM tmp_tablename_fdw
-- WHERE condictions
;
Вам нужно подготовить базу данных для FDW, и вместо статической tmp_tablename_fdw
вы можете использовать функцию, которая ее генерирует :
CREATE EXTENSION file_fdw;
CREATE SERVER import FOREIGN DATA WRAPPER file_fdw;
CREATE FOREIGN TABLE tmp_tablename_fdw(
...
) SERVER import OPTIONS ( filename '/tmp/pg_io/file.csv', format 'csv');
Пример JSON . Набор из двух файлов, myRawData1.json
и Ranger_Policies2.json
может поступать в организм с помощью:
INSERT INTO tablename (fname, metadata, content)
SELECT fname, meta, j -- do any data transformation here
FROM jsonb_read_files('myRawData%.json')
-- WHERE any_condiction_here
;
где функция jsonb_read_files () читает все файлы папки, определенные маской:
CREATE or replace FUNCTION jsonb_read_files(
p_flike text, p_fpath text DEFAULT '/tmp/pg_io/'
) RETURNS TABLE (fid int, fname text, fmeta jsonb, j jsonb) AS $f$
WITH t AS (
SELECT (row_number() OVER ())::int id,
f as fname,
p_fpath ||'/'|| f as f
FROM pg_ls_dir(p_fpath) t(f)
WHERE f like p_flike
) SELECT id, fname,
to_jsonb( pg_stat_file(f) ) || jsonb_build_object('fpath',p_fpath),
pg_read_file(f)::jsonb
FROM t
$f$ LANGUAGE SQL IMMUTABLE;
Наиболее частый метод «загрузки файлов» (главным образом в больших данных) - это сохранение исходного файла в формате gzip и передача его с помощью потокового алгоритма , всего, что может работать быстро и без использования диска в каналах Unix:
gunzip remote_or_local_file.csv.gz | convert_to_sql | psql
Таким образом, идеальный (будущий) вариант сервера для формата .csv.gz
.