Wie kann man in Postgresql effizient Millionen von Zeilen von einer Tabelle in eine andere kopieren?

36

Ich habe zwei Datenbanktabellen. Eine enthält Hunderte von Millionen von Datensätzen. Nennen wir das mal history. Der andere wird täglich berechnet und ich möchte alle seine Datensätze in den historyeinen kopieren .

Was ich getan habe war zu rennen:

INSERT INTO history SELECT * FROM daily

Und es hat eine Weile geklappt, aber es wurde langsamer und langsamer, als die Anzahl der Rekorde immer größer wurde. Jetzt habe ich ungefähr 2 Millionen Datensätze, die in einem Arbeitsgang von dailynach kopiert werden müssen, historyund die Fertigstellung dauert zu lange.

Gibt es eine andere, effizientere Möglichkeit, Daten von einer Tabelle in eine andere zu kopieren?

Milovan Zogovic
quelle

Antworten:

10

Wenn Sie vorhaben, den Verlauf für lange Zeiträume (viele Monate) aufzubewahren, schlage ich vor, Partitionsoptionen zu prüfen - kann eine Partition für jeden Tag oder jede Woche usw. sein. Dies hängt auch von den Zugriffsmustern Ihrer Verlaufstabelle ab (führen Sie Abfragen aus, die datumsübergreifend auf Daten zugreifen? Führen Sie viele Aggregationen usw. durch). Schauen Sie sich materialisierte Ansichten zum Speichern von Aggregaten / Zusammenfassungen an. http://www.postgresql.org/docs/9.3/static/ddl-partitioning.html http://www.postgresql.org/docs/9.3/static/sql-creatematerializedview.html

Jayadevan
quelle
Danke für die Antwort. Es scheint der einzige Weg zu sein. Ich müsste die Daten nach Monaten partitionieren und so die Neuindizierung (da die Indexregenerierung hier ein Problem war) viel schneller durchführen.
Milovan Zogovic
16

Sichern Sie die Tabelle im CSV-Format

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

Verwenden Sie den Befehl COPY, der für große Datenmengen weitaus effizienter ist.

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

Weitere Informationen finden Sie in den Postgres-Dokumenten unter http://www.postgresql.org/docs/current/static/sql-copy.html

Fabrizio Mazzoni
quelle
1
Es läuft immer noch sehr, sehr langsam ... Vielleicht hat es etwas damit zu tun, einen so großen Index neu erstellen zu müssen? Die historyTabelle enthält 160 Millionen Zeilen , und wir fügen drei weitere Millionen Zeilen hinzu.
Milovan Zogovic
2
Wenn Sie eine leere Tabelle füllen oder mehr Zeilen hinzufügen, als bereits vorhanden sind, ist es in der Regel effizienter, nicht gruppierte Indizes zu löschen und nach Abschluss der Übertragung neu zu erstellen (es sei denn, die Tabelle (n) werden zurzeit aktiv verwendet )
David Spillett
Übrigens, ist dies eine einmalige Operation oder müssen Sie sie regelmäßig durchführen? Wenn es regelmäßig ist, empfehle ich, dass Sie einen Auslöser erstellen, damit Sie diese Prüfung nicht jedes Mal durchlaufen müssen.
Fabrizio Mazzoni
@FabrizioMazzoni - Es muss täglich zu einer bestimmten Zeit durchgeführt werden (ein bisschen Schnappschüsse in der Zeit machen).
Milovan Zogovic
@ DavidSpillett - in der Tat! Durch das Löschen von Indizes wird der Import sehr schnell (siehe meine Antwort oben). Die Neuerstellung von Indizes dauert jedoch Stunden (da ich
160 Millionen
13

Das Problem war mit Indizes. Die historyTabelle hatte 160 Millionen indizierte Zeilen. Durch Ausführen von entweder COPY FROModer INSERT INTO .. SELECTdauerte es viel Zeit, keine Zeilen einzufügen, sondern Indizes zu aktualisieren. Wenn ich Indizes deaktivierte, importierte es 3M Zeilen in 10 Sekunden. Jetzt muss ich einen schnelleren Weg finden, um den großen Tisch neu zu indizieren.

Milovan Zogovic
quelle
3
Benötigen Sie überhaupt Indizes für eine Verlaufstabelle?
Sherlock,
2
Fügen Sie den Index mit dem Schlüsselwort
CONCURRENTLY
10

Sie können psql- Tool verwenden, ich könnte effizient sein, wie die folgenden,

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"

Sie können auch ein Shell-Skript schreiben.

Franken
quelle
Tolle Lösung ohne Zwischendatei. Sehr schnell kopierte ich eine 950-Millionen-Zeilen-Tabelle in 1h20 (ohne Indizes) zwischen normaler Festplatte und Netzwerkdateisystem.
Le Droid
3

Dies ist natürlich keine exakte Antwort auf Ihre Frage, aber wenn Sie nicht auf die historyTabelle zugreifen müssen , können Sie auch einen SQL-Dump generieren:

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

Dann könnte man mit einem Tool gerne gitdie Differenz berechnen und diese effizient speichern.

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

Dies ist nützlich, da sich die meisten Teile in einer Datenbank nicht jeden Tag ändern. Anstatt für jeden Tag eine vollständige Kopie zu speichern, kann die Differenz zwischen zwei Tagen gespeichert werden.

Sie können einen crontabJob so verwenden, dass der Dump jeden Tag verarbeitet wird.

Willem Van Onsem
quelle