Ich versuche, den gesamten Inhalt einer großen Tabelle über die Befehlszeile mit zu sichern pqsl
, stoße jedoch auf ein Problem, bei dem die Speichernutzung bis zu dem Punkt steigt, an dem der Prozess beendet wird, bevor überhaupt Daten ausgegeben werden.
Was ich nicht verstehe, ist: Warum gibt die Abfrage nicht sofort Ergebnisse zurück und wird abgeschlossen, ohne dass der Speicher knapp wird?
Hier ist eine Erklärung genau dessen, was ich versuche:
Ich habe einen Tisch, sag:
CREATE TABLE big
(
id integer,
rand double precision
)
Eine große Anzahl von Zeilen wird eingefügt (50 Millionen):
insert into big
select generate_series(1, 50000000) AS id, random();
Der Abfrageplan zum Auswählen jeder Zeile sieht wie folgt aus (nicht überraschend):
$ psql -d big -c "explain select * from big;"
QUERY PLAN
----------------------------------------------------------------
Seq Scan on big (cost=0.00..924326.24 rows=50000124 width=12)
(1 row)
Ich versuche dann, den Inhalt in eine Datei zu kopieren:
$ psql -d big -c "select * from big;" > big.dump
Wie ich oben sagte, schlägt dieser Befehl fehl, bevor Daten geschrieben werden, anscheinend indem immer mehr Speicher belegt wird, bevor er vom Betriebssystem getötet wird (von "OOM Killer").
Hinweis: Ich verstehe, dass ich damit pg_dump
etwas Ähnliches erreichen könnte, aber in Wirklichkeit ist meine Abfrage komplexer als diese - insbesondere möchte ich jede Zeile beim Dumping als JSON codieren .
Einige Konfigurationsdetails:
- postgresql version = 9.3.4
- work_mem = 1 MB
- shared_buffers = 128 MB
- effektive_cache_size = 128MB
COPY
psql -d big -c "copy (select * from big) to stdout" > big.dump
psql
Prozess oder der Postgres-Backend-Prozess für Ihre Verbindung? Ich denke, der client (psql
) puffert das Ergebnis irgendwie (oder erzwingt den Backend-Prozess dazu). Wenn Siecopy
die Daten verwenden, werden sie niemals an den Client (daspsql
Programm) übertragen, da dies alles auf der Serverseite erfolgt.psql
Prozess aus Syslog :Out of memory: Kill process 26465 (psql)
. Zu Ihrer Information: Ich führe den Client auf demselben Computer wie den Server aus.psql
- es ist immer noch ein "Client" für den Server. Tritt dies auch auf, wenn Sie den\o
Befehl verwenden, um die Ausgabe in eine Datei zu schreiben? In diesem Fallpsql
"weiß", dass Sie die Daten nicht anzeigen müssen, möglicherweise müssen die Daten dann effizienter abgerufen werden.Antworten:
Standardmäßig werden die Ergebnisse aus zwei Gründen vollständig im Speicher gepuffert:
1) Wenn diese
-A
Option nicht verwendet wird , werden die Ausgabezeilen so ausgerichtet, dass die Ausgabe erst gestartet werden kann, wenn psql die maximale Länge jeder Spalte kennt. Dies bedeutet, dass jede Zeile besucht wird (was neben viel Speicher auch eine erhebliche Zeit in Anspruch nimmt).2) Sofern nicht a angegeben
FETCH_COUNT
, verwendet psql die SynchronfunktionPQexec
direkt in der Abfrage, wodurch die gesamte Ergebnismenge gepuffert wird. Beim Festlegen von aFETCH_COUNT
wird jedoch eine Cursor-basierte Methode mit aufeinanderfolgenden Abrufaufrufen verwendet, bei der der clientseitige Puffer in allenFETCH_COUNT
Zeilen freigegeben oder wiederverwendet wird .Eine große Ergebnismenge sollte also mit einem Befehl wie dem folgenden abgerufen werden:
Mit
FETCH_COUNT
reduziert, wenn die Zeilen sehr groß sind und es immer noch zu viel Speicher verbraucht.Das
-t
steht für--tuples-only
, was die Ausgabe von Kopf- und Fußzeilen unterdrückt.quelle
-A
keinen großen Unterschied macht, obwohl meine Tabelle einfach ist (dh nur zwei Zahlenspalten) - könnte es für eine Tabelle mit "breiterer" oder variabler Breite einen größeren Unterschied machen? Die EinstellungFETCH_COUNT
macht jedoch den Unterschied.