it-swarm.com.de

Langsame einfache Aktualisierungsabfrage für die PostgreSQL-Datenbank mit 3 Millionen Zeilen

Ich versuche einen einfachen UPDATE table SET column1 = 0 in einer Tabelle mit ~ 3 Millionen Zeilen in Postegres 8.4, aber es dauert ewig, bis ich fertig bin. Es läuft seit mehr als 10 min. jetzt in meinem letzten Versuch.

Vorher habe ich versucht, einen VACUUM- und ANALYZE-Befehl für diese Tabelle auszuführen, und ich habe auch versucht, einige Indizes zu erstellen (obwohl ich bezweifle, dass dies in diesem Fall einen Unterschied ausmacht), aber es scheint nichts zu helfen.

Irgendwelche anderen Ideen?

Danke, Ricardo

Update:

Dies ist die Tabellenstruktur:

CREATE TABLE myTable
(
  id bigserial NOT NULL,
  title text,
  description text,
  link text,
  "type" character varying(255),
  generalFreq real,
  generalWeight real,
  author_id bigint,
  status_id bigint,
  CONSTRAINT resources_pkey PRIMARY KEY (id),
  CONSTRAINT author_pkey FOREIGN KEY (author_id)
      REFERENCES users (id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION,
  CONSTRAINT c_unique_status_id UNIQUE (status_id)
);

Ich versuche, UPDATE myTable SET generalFreq = 0; auszuführen.

26
Ricardo Lage

Sehen Sie sich diese Antwort an: PostgreSQL ist langsam an einer großen Tabelle mit Arrays und vielen Updates

Beginnen Sie zuerst mit einem besseren FILLFACTOR, führen Sie VACUUM FULL aus, um das Umschreiben der Tabelle zu erzwingen, und überprüfen Sie die HOT-Updates nach Ihrer UPDATE-Abfrage:

SELECT n_tup_hot_upd, * FROM pg_stat_user_tables WHERE relname = 'myTable';

Heiße Aktualisierungen sind viel schneller, wenn Sie viele Datensätze aktualisieren müssen. Weitere Informationen zu HOT finden Sie in diesem Artikel .

Ps. Sie benötigen Version 8.3 oder besser.

13
Frank Heikens

Ich muss Tabellen mit 1 oder 2 Milliarden Zeilen mit verschiedenen Werten für jede Zeile aktualisieren. Bei jedem Durchlauf werden ~ 100 Millionen Änderungen (10%) vorgenommen. Mein erster Versuch bestand darin, sie in einer Transaktion mit 300K-Aktualisierungen direkt auf einer bestimmten Partition zu gruppieren, da Postgresql vorbereitete Abfragen nicht immer optimiert, wenn Sie Partitionen verwenden.

  1. Transaktionen des Bündels von "UPDATE myTable SET myField = value WHERE MyId = id"
    Gibt 1.500 Updates/Sek. was bedeutet, dass jeder Lauf mindestens 18 Stunden dauern würde.
  2. HOT aktualisiert Lösung wie hier beschrieben mit FILLFACTOR = 50. Gibt 1.600 Updates/Sek. Ich verwende SSDs, daher ist dies eine kostspielige Verbesserung, da Die Speichergröße verdoppelt.
  3. Fügen Sie eine temporäre Tabelle mit aktualisierten Werten ein und führen Sie sie nach Mit UPDATE ... FROM aus 18.000 updates/sec. wenn ich für jede Partition einen VACUUM mache; Sonst 100.000 up/s. Cooool.
    Hier ist die Sequenz von Operationen:

CREATE TEMP TABLE tempTable (id BIGINT NOT NULL, field(s) to be updated,
CONSTRAINT tempTable_pkey PRIMARY KEY (id));

Sammeln Sie eine Reihe von Aktualisierungen in einem Puffer abhängig vom verfügbaren RAM Wenn es gefüllt ist oder die Tabelle/Partition geändert werden muss oder abgeschlossen ist:

COPY tempTable FROM buffer;
UPDATE myTable a SET field(s)=value(s) FROM tempTable b WHERE a.id=b.id;
COMMIT;
TRUNCATE TABLE tempTable;
VACUUM FULL ANALYZE myTable;

Das bedeutet, dass ein Lauf jetzt für 100 Millionen Updates 1,5 Stunden statt 18 Stunden benötigt, einschließlich Vakuum.

28
Le Droid

Nach 35 min warten Damit meine UPDATE-Abfrage abgeschlossen wurde (und immer noch nicht beendet wurde), entschied ich mich, etwas anderes auszuprobieren. Was ich also tat, war ein Befehl:

CREATE TABLE table2 AS 
SELECT 
  all the fields of table1 except the one I wanted to update, 0 as theFieldToUpdate
from myTable

Fügen Sie dann Indizes hinzu, löschen Sie die alte Tabelle und benennen Sie die neue um, um ihren Platz zu übernehmen. Das dauerte nur 1,7 min. zu verarbeiten und etwas mehr Zeit, um die Indizes und Einschränkungen neu zu erstellen. Aber es hat geholfen! :)

Das funktionierte natürlich nur, weil sonst niemand die Datenbank verwendete. Ich müsste den Tisch zuerst sperren, wenn dies in einer Produktionsumgebung war.

7
Ricardo Lage

Heute habe ich viele Stunden mit ähnlichen Problemen verbracht. Ich habe eine Lösung : alle Constraints/Indizes vor dem Update abgelegt. Unabhängig davon, ob die zu aktualisierende Spalte indiziert ist oder nicht, es scheint, als würde psql alle Indizes für alle aktualisierten Zeilen aktualisieren. Fügen Sie nach Abschluss des Updates die Einschränkungen/Indizes wieder hinzu.

2
Tregoreg

Versuchen Sie folgendes (beachten Sie, dass generalFreq mit dem Typ REAL beginnt und gleich bleibt):

ALTER TABLE myTable ALTER COLUMN generalFreq TYPE REAL USING 0;

Dadurch wird die Tabelle ähnlich wie bei DROP + CREATE neu geschrieben, und alle Indizes werden neu erstellt. Aber alles in einem Befehl. Sehr viel schneller (ungefähr 2x), und Sie müssen sich nicht mit Abhängigkeiten und der Neuerstellung von Indizes und anderen Dingen befassen, obwohl die Tabelle für die Dauer gesperrt wird (Zugriff exklusiv - d. H. Vollständige Sperre). Oder vielleicht ist es das, was Sie wollen, wenn Sie möchten, dass alles andere dahinter steht. Wenn Sie nicht "zu viele" Zeilen auf diese Weise aktualisieren, ist dies langsamer als nur ein Update.

2
Fabiano Bonin

Wie läufst du es? Wenn Sie jede Zeile in einer Schleife ausführen und eine Aktualisierungsanweisung ausführen, führen Sie möglicherweise Millionen von Einzelupdates aus, weshalb sie unglaublich langsam ausgeführt wird.

Wenn Sie eine einzige Aktualisierungsanweisung für alle Datensätze in einer Anweisung ausführen, wird sie viel schneller ausgeführt. Wenn dieser Prozess langsam ist, liegt dies wahrscheinlich an Ihrer Hardware. 3 Millionen sind viele Rekorde.

0
Tom Gullen

In meinen Tests habe ich festgestellt, dass ein großes Update, mehr als 200 000 Zeilen, langsamer ist als 2 Updates von 100 000 Zeilen, selbst mit einer temporären Tabelle.

Meine Lösung ist, in einer Schleife eine temporäre Tabelle mit 200.000 Zeilen zu erstellen. In dieser Tabelle berechne ich meine Werte und aktualisiere dann meine Haupttabelle mit den neuen Werten usw.

Alle 2.000.000 Zeilen habe ich manuell "VACUUM ANALYZE mytable", ich habe festgestellt, dass der automatische Staubsauger seine Aufgabe für solche Updates nicht erfüllt.

0
Rolintocour

Das erste, was ich vorschlagen würde (von https://dba.stackexchange.com/questions/118178/does-updating-a-row-with-the-selbe-value-actual-update-the-row ) soll nur Zeilen aktualisieren, die es "brauchen", zB:

 UPDATE myTable SET generalFreq = 0 where generalFreq != 0;

(Möglicherweise benötigen Sie auch einen Index auf generalFreq). Dann werden Sie weniger Zeilen aktualisieren. Allerdings nicht, wenn die Werte bereits alle nicht Null sind, aber das Aktualisieren von weniger Zeilen kann "helfen", da sie andernfalls und alle Indizes unabhängig davon aktualisiert werden, ob sich der Wert geändert hat oder nicht.

Eine weitere Option: Wenn die Sterne in Bezug auf Standardwerte und Nicht-Null-Beschränkungen ausgerichtet sind, können Sie die alte Spalte löschen und eine andere erstellen, indem Sie einfach die Metadaten (Instant Time) anpassen.

0
rogerdpack