Speichernutzung bei Auswahl für eine große Anzahl von Zeilen

7

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_dumpetwas Ä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
JonoB
quelle
Obwohl dies die Frage nicht beantwortet, konnte ich die COPYpsql -d big -c "copy (select * from big) to stdout" > big.dump
vorliegende
Welcher Prozess wird genau getötet? Der psqlProzess 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 Sie copydie Daten verwenden, werden sie niemals an den Client (das psqlProgramm) übertragen, da dies alles auf der Serverseite erfolgt.
a_horse_with_no_name
Sieht aus wie der psqlProzess aus Syslog : Out of memory: Kill process 26465 (psql). Zu Ihrer Information: Ich führe den Client auf demselben Computer wie den Server aus.
JonoB
Es spielt keine Rolle, wo ausgeführt wird psql - es ist immer noch ein "Client" für den Server. Tritt dies auch auf, wenn Sie den \oBefehl verwenden, um die Ausgabe in eine Datei zu schreiben? In diesem Fall psql"weiß", dass Sie die Daten nicht anzeigen müssen, möglicherweise müssen die Daten dann effizienter abgerufen werden.
a_horse_with_no_name

Antworten:

8

Standardmäßig werden die Ergebnisse aus zwei Gründen vollständig im Speicher gepuffert:

1) Wenn diese -AOption 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 Synchronfunktion PQexecdirekt in der Abfrage, wodurch die gesamte Ergebnismenge gepuffert wird. Beim Festlegen von a FETCH_COUNTwird jedoch eine Cursor-basierte Methode mit aufeinanderfolgenden Abrufaufrufen verwendet, bei der der clientseitige Puffer in allen FETCH_COUNTZeilen freigegeben oder wiederverwendet wird .

Eine große Ergebnismenge sollte also mit einem Befehl wie dem folgenden abgerufen werden:

psql -A -t --variable="FETCH_COUNT=10000" \
     -c "select columns from bigtable" \
     > output-file

Mit FETCH_COUNTreduziert, wenn die Zeilen sehr groß sind und es immer noch zu viel Speicher verbraucht.

Das -tsteht für --tuples-only, was die Ausgabe von Kopf- und Fußzeilen unterdrückt.

Daniel Vérité
quelle
Hervorragende Erklärung. Ich habe sowohl 1) als auch 2) getrennt und zusammen getestet. Es stellt sich heraus, dass die Angabe -Akeinen 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 Einstellung FETCH_COUNT macht jedoch den Unterschied.
JonoB
Ja, der Effekt der Spaltenausrichtung ist stärker ausgeprägt, wenn das Ergebnis Breittextspalten unterschiedlicher Länge enthält. Es geht jedoch darum, die Ausrichtung zu deaktivieren, um den Rohinhalt zu extrahieren, anstatt sich die mit Leerzeichen aufgefüllte Version davon auf einem Terminal anzusehen.
Daniel Vérité
Aus einigen kleinen Experimenten geht hervor, dass beim Festlegen von FETCH_COUNT nur die Headergrößen basierend auf "bisher am größten pro Spalte" ausgerichtet werden (dh wenn Sie FETCH_COUNT angeben, müssen Sie sich keine großen Gedanken über die Ausrichtung machen). FWIW ...
rogerdpack