Как эффективно скопировать миллионы строк из одной таблицы в другую в Postgresql?

37

У меня есть две таблицы базы данных. Один содержит сотни миллионов записей. Позвоните этому history. Другой рассчитывается ежедневно, и я хочу скопировать все его записи в historyодин.

Что я сделал, чтобы бежать:

INSERT INTO history SELECT * FROM daily

И некоторое время это делало свое дело, но оно становилось все медленнее и медленнее, так как количество записей продолжало расти. Теперь у меня есть около 2 миллионов записей , которые должны быть скопированы с dailyк historyв одной операции , и это занимает слишком много времени , чтобы закончить.

Есть ли другой, более эффективный способ копирования данных из одной таблицы в другую?

Милован Зогович
источник

Ответы:

10

Если вы планируете хранить историю в течение длительных периодов (много месяцев), я предлагаю взглянуть на варианты разбиения - может быть один раздел на каждый день или неделю и так далее. Это зависит также от шаблонов доступа к вашей таблице истории (выполняете ли вы запросы, которые обращаются к данным по датам? У вас много агрегаций и т. Д.). Посмотрите на материализованные представления для хранения агрегатов / сводок. http://www.postgresql.org/docs/9.3/static/ddl-partitioning.html http://www.postgresql.org/docs/9.3/static/sql-creatematerializedview.html

Jayadevan
источник
Спасибо за ответ. Кажется, это единственный путь. Мне нужно было бы разделить данные по месяцам и, таким образом, сделать переиндексацию (так как регенерация индекса была проблемой здесь) намного быстрее.
Милован Зогович
16

Дамп таблицы в формате CSV

COPY table TO '/tmp/table.csv' DELIMITER ',';

используйте команду COPY, которая гораздо более эффективна для больших объемов данных.

COPY table FROM '/tmp/table.csv' DELIMITER ',';

Проверьте postgres docs на http://www.postgresql.org/docs/current/static/sql-copy.html для получения дополнительной информации.

Фабрицио Маззони
источник
1
Он по-прежнему работает очень, очень медленно ... Возможно, это связано с необходимостью перестроить такой огромный индекс? В historyтаблице 160 миллионов строк , и мы добавляем еще 3 миллиона строк.
Милован Зогович
2
Если вы заполняете пустую таблицу или добавляете больше строк, чем уже существует, как правило, более эффективно отбрасывать некластеризованные индексы и воссоздавать их после завершения передачи (если только в этот момент таблицы не используются активно). )
Дэвид Спиллетт
Кстати, это разовая операция или это то, что вы должны делать регулярно? Если это регулярно, я рекомендую вам создать триггер, чтобы вам не приходилось каждый раз проходить через это испытание.
Фабрицио Маззони
@FabrizioMazzoni - это должно выполняться ежедневно в определенное время (вроде снимки во времени).
Милован Зогович
@DavidSpillett - действительно! Удаление индексов делает импорт очень быстрым (см. Мой ответ выше), однако воссоздание индексов занимает часы (так как у меня 160M строк в базе данных) ..
Милован Зогович
14

Проблема была с индексами. historyТаблица была 160M индексированных строк. Запустив или, COPY FROMили INSERT INTO .. SELECTпотребовалось много времени не для вставки строк, а для обновления индексов. Когда я отключил индексы, он импортировал 3M строк за 10 секунд. Теперь мне нужно найти более быстрый способ переиндексации большого стола.

Милован Зогович
источник
3
Вам даже нужны индексы для таблицы истории?
Шерлок
2
Добавьте индекс, используя ключевое слово
CONCURRENTLY
11

Вы можете использовать инструмент PSQL , я мог бы быть эффективным, как показано ниже,

psql -h ${DAILY_HOST_IP} -p ${PG_PORT} ${DB_NAME} ${USER_NAME} -c "copy daily to stdout " | psql -h ${HISTORY_HOST_IP} -p ${PG_PORT} ${DB_NAME} ${USER_NAME}  -c "copy history from stdin"

Также вы можете написать сценарий оболочки.

франки
источник
Отличное решение без промежуточного файла. Очень быстро я скопировал таблицу с 950 миллионами строк за 1 час 20 минут (без индексов) между обычным диском и сетевой файловой системой.
Le Droid
Жаль, что это невозможно сделать напрямую с одного стола на другой.
Чарли Кларк
3

Это, конечно, не точный ответ на ваш вопрос, но если вам не нужен доступ к historyтаблице, вы также можете сгенерировать дамп SQL:

pg_dump -h host -p port -w -U user db > dump.sql

Затем можно использовать такой инструмент, как gitрассчитать разницу и эффективно ее сохранить.

git add dump.sql
git commit -m "temp dump"
git gc --aggressive

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

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

Виллем Ван Онсем
источник