Como copiar com eficiência milhões de linhas de uma tabela para outra no Postgresql?

37

Eu tenho duas tabelas de banco de dados. Um contém centenas de milhões de registros. Vamos chamar assim history. O outro é calculado diariamente e quero copiar todos os seus registros para historyesse.

O que eu fiz foi correr:

INSERT INTO history SELECT * FROM daily

E fez o truque por um tempo, mas começou a ficar cada vez mais lento à medida que o número de registros continuava crescendo. Agora, tenho cerca de 2 milhões de registros que precisam ser copiados de dailypara uma historyúnica operação e leva muito tempo para ser concluído.

Existe outra maneira mais eficiente de copiar dados de uma tabela para outra?

Milovan Zogovic
fonte

Respostas:

10

Se você planeja manter o histórico por longos períodos (muitos meses), sugiro dar uma olhada nas opções de particionamento - pode haver uma partição para cada dia ou semana e assim por diante. Depende também dos padrões de acesso da sua tabela de histórico (você executa consultas que acessam dados através de datas? Você faz muitas agregações etc.). Dê uma olhada nas visualizações materializadas para armazenar agregados / resumos. http://www.postgresql.org/docs/9.3/static/ddl-partitioning.html http://www.postgresql.org/docs/9.3/static/sql-creatematerializedview.html

Jayadevan
fonte
Obrigado pela resposta. Parece ser o único caminho a percorrer. Eu precisaria particionar os dados por meses e, assim, tornar a reindexação (uma vez que a regeneração do índice era um problema aqui) muito mais rápido.
Milovan Zogovic
16

Despejar a tabela no formato csv

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

use o comando COPY, que é muito mais eficiente para grandes quantidades de dados.

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

Verifique os documentos do postgres em http://www.postgresql.org/docs/current/static/sql-copy.html para obter mais informações

Fabrizio Mazzoni
fonte
11
Ainda está funcionando muito, muito devagar ... Talvez tenha que fazer alguma coisa para ter que reconstruir um índice tão grande? Existem 160 milhões de linhas na historytabela e anexamos mais 3 milhões de linhas.
Milovan Zogovic
2
Como você está preenchendo uma tabela vazia ou adicionando mais linhas do que as que existem, geralmente é mais eficiente descartar índices não agrupados em cluster e recriá-los quando a transferência estiver concluída (a menos que haja um uso ativo da (s) tabela (s) no momento )
David Spillett
BTW, esta é uma operação única ou é algo que você deve fazer regularmente? Se for regularmente, sugiro que você crie um gatilho para não ter que passar por essa provação toda vez.
Fabrizio Mazzoni
@FabrizioMazzoni - Ele deve ser realizado diariamente em horário específico (meio que tirando instantâneos no tempo).
Milovan Zogovic
@DavidSpillett - de fato! Largando índices marcas importar muito rápido (ver minha resposta acima), no entanto, recriando índices leva horas (desde que eu tenho 160M linhas no banco de dados) ..
Milovan Zogovic
14

O problema estava com os índices. A historytabela tinha 160 milhões de linhas indexadas. Ao executar um COPY FROMou INSERT INTO .. SELECTlevava muito tempo, não para inserir linhas, mas para atualizar índices. Quando desabilitei os índices, ele importou 3 milhões de linhas em 10 segundos. Agora, preciso encontrar uma maneira mais rápida de reindexar a grande mesa.

Milovan Zogovic
fonte
3
Você precisa de índices em uma tabela de histórico?
Sherlock
2
Adicione o índice usando a palavra
CONCURRENTLY
11

Você pode usar a ferramenta psql , posso ser eficiente, como a seguir,

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"

Além disso, você pode escrever um script de shell.

francos
fonte
Ótima solução sem arquivo intermediário. Muito rápido também, copiei uma tabela de 950 milhões de linhas em 1h20 (sem índices) entre o disco normal e o sistema de arquivos da rede.
Le Droid
É uma pena que isso não possa ser feito diretamente de uma mesa para outra.
Charlie Clark
3

Obviamente, essa não é uma resposta exata para sua pergunta, mas se você não precisar acessar a historytabela, também poderá gerar um dump SQL:

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

Então, pode-se usar uma ferramenta como gitcalcular a diferença e armazená-la com eficiência.

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

Isso é útil porque a maioria das partes de um banco de dados não muda todos os dias. Em vez de armazenar uma cópia inteira para todos os dias, é possível armazenar a diferença entre dois dias.

Você pode usar um crontabtrabalho para que o despejo seja processado todos os dias.

Willem Van Onsem
fonte