Come copiare in modo efficiente milioni di righe da una tabella all'altra in Postgresql?


37

Ho due tabelle di database. Uno contiene centinaia di milioni di record. Chiamiamolo history. L'altro è calcolato su base giornaliera e voglio copiare tutti i suoi record historynell'uno.

Quello che ho fatto è stato quello di eseguire:

INSERT INTO history SELECT * FROM daily

E ha funzionato per un po ', ma ha iniziato a diventare sempre più lento man mano che il numero di record continuava a crescere. Ora ho circa 2 milioni di dischi che devono essere copiati da dailya historyin un'unica operazione e ci vuole troppo tempo per essere completato.

Esiste un altro modo più efficiente di copiare i dati da una tabella all'altra?

Risposte:


10

Se prevedi di conservare la cronologia per lunghi periodi (molti mesi), ti suggerisco di dare un'occhiata alle opzioni di partizionamento: potrebbe essere una partizione per ogni giorno o settimana e così via. Dipende anche dai modelli di accesso della tabella della cronologia (esegui query che accedono ai dati tra le date? Esegui molte aggregazioni, ecc.). Dai un'occhiata alle viste materializzate per l'archiviazione di aggregati / riepiloghi. http://www.postgresql.org/docs/9.3/static/ddl-partitioning.html http://www.postgresql.org/docs/9.3/static/sql-creatematerializedview.html


Grazie per la risposta. Sembra l'unica strada da percorrere. Avrei bisogno di partizionare i dati per mesi e quindi rendere la reindicizzazione (poiché la rigenerazione dell'indice era un problema qui) molto più veloce.
Milovan Zogovic,

16

Dump della tabella in formato CSV

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

utilizzare il comando COPIA, che è molto più efficiente per grandi quantità di dati.

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

Controlla i documenti di Postgres su http://www.postgresql.org/docs/current/static/sql-copy.html per maggiori informazioni


1
Funziona ancora molto, molto lentamente ... Forse deve fare qualcosa per dover ricostruire un indice così grande? Ci sono 160 milioni di righe nella historytabella e stiamo aggiungendo altri 3 milioni di righe.
Milovan Zogovic,

2
Se stai riempiendo una tabella vuota o aggiungendo più righe di quelle già esistenti, in genere è più efficiente eliminare gli indici non cluster e ricrearli una volta completato il trasferimento (a meno che al momento non vi sia un uso attivo delle tabelle) )
David Spillett il

A proposito, si tratta di un'operazione una tantum o è qualcosa che devi fare regolarmente? Se è su base regolare, ti consiglio di creare un trigger in modo da non dover passare attraverso questa prova ogni volta.
Fabrizio Mazzoni,

@FabrizioMazzoni - Deve essere eseguito su base giornaliera in un momento specifico (un po 'scattare istantanee in tempo).
Milovan Zogovic il

@DavidSpillett - davvero! La caduta degli indici rende l'importazione molto veloce (vedi la mia risposta sopra), tuttavia, ricreare gli indici richiede ore (dal momento che ho 160M righe nel database) ..
Milovan Zogovic

14

Il problema era con gli indici. La historytabella aveva 160 M di righe indicizzate. Eseguendo uno COPY FROMo INSERT INTO .. SELECTci voleva molto tempo non per inserire le righe, ma per aggiornare gli indici. Quando ho disabilitato gli indici, ha importato 3 milioni di righe in 10 secondi. Ora ho bisogno di trovare un modo più veloce di reindicizzare il grande tavolo.


3
Hai anche bisogno di indici su una tabella della cronologia?
Sherlock,

2
Aggiungi l'indice utilizzando la parola chiave CONCURRENTLY
Akvel

11

Puoi usare lo strumento psql , potrei essere efficiente, come il seguente,

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"

Inoltre puoi scrivere uno script di shell.


Ottima soluzione senza file intermedio. Molto veloce, ho copiato una tabella di 950 milioni di righe in 1h20 (senza indici) tra il normale disco e il file system di rete.
Le Droid,

È un vero peccato che non si possa fare direttamente da un tavolo all'altro.
Charlie Clark

3

Questa non è ovviamente una risposta esatta alla tua domanda, ma se non hai bisogno di accedere alla historytabella, puoi anche generare un dump SQL:

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

Quindi si potrebbe usare uno strumento come gitcalcolare la differenza e memorizzarlo in modo efficiente.

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

Ciò è utile perché la maggior parte delle parti di un database non cambierà ogni giorno. Invece di archiviare un'intera copia per ogni giorno, si può memorizzare la differenza tra due giorni.

È possibile utilizzare un crontablavoro in modo tale che il dump venga elaborato ogni giorno.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.