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?

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


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


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.


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.


Ó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.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.