it-swarm.com.de

Warum hat Postgres UPDATE 39 Stunden gedauert?

Ich habe eine Postgres-Tabelle mit ~ 2,1 Millionen Zeilen. Ich habe das folgende Update ausgeführt:

WITH stops AS (
    SELECT id,
           rank() OVER (ORDER BY offense_timestamp,
                     defendant_dl,
                     offense_street_number,
                     offense_street_name) AS stop
    FROM   consistent.master
    WHERE  citing_jurisdiction=1
)

UPDATE consistent.master
SET arrest_id=stops.stop
FROM stops
WHERE master.id = stops.id;

Die Ausführung dieser Abfrage dauerte 39 Stunden. Ich verwende dies auf einem 4 (physischen) i7 Q720-Laptop-Prozessor mit Kern, viel RAM und nichts anderes, das die meiste Zeit ausgeführt wird. Keine Platzbeschränkungen für die Festplatte. Der Tisch wurde kürzlich gesaugt, analysiert und neu indiziert.

Während der gesamten Ausführung der Abfrage, zumindest nach Abschluss des ersten WITH, war die CPU-Auslastung normalerweise gering und die Festplatte zu 100% ausgelastet. Die Festplatte wurde so stark genutzt, dass jede andere App erheblich langsamer als normal lief.

Die Energieeinstellung des Laptops war aktiviert Hohe Leistung (Windows 7 x64).

Hier ist die Erklärung:

Update on master  (cost=822243.22..1021456.89 rows=2060910 width=312)
  CTE stops
    ->  WindowAgg  (cost=529826.95..581349.70 rows=2060910 width=33)
          ->  Sort  (cost=529826.95..534979.23 rows=2060910 width=33)
                Sort Key: consistent.master.offense_timestamp, consistent.master.defendant_dl, consistent.master.offense_street_number, consistent.master.offense_street_name
                ->  Seq Scan on master  (cost=0.00..144630.06 rows=2060910 width=33)
                      Filter: (citing_jurisdiction = 1)
  ->  Hash Join  (cost=240893.51..440107.19 rows=2060910 width=312)
        Hash Cond: (stops.id = consistent.master.id)
        ->  CTE Scan on stops  (cost=0.00..41218.20 rows=2060910 width=48)
        ->  Hash  (cost=139413.45..139413.45 rows=2086645 width=268)
              ->  Seq Scan on master  (cost=0.00..139413.45 rows=2086645 width=268)

citing_jurisdiction=1 Schließt nur einige Zehntausende von Zeilen aus. Selbst mit dieser WHERE -Klausel arbeite ich immer noch mit über 2 Millionen Zeilen.

Die Festplatte ist mit TrueCrypt 7.1a vollständig mit dem Laufwerk verschlüsselt. Das verlangsamt die Dinge ein wenig, reicht aber nicht aus, um eine Abfrage zu veranlassen , die viele Stunden dauert.

Die Ausführung des Teils WITH dauert nur etwa 3 Minuten.

Das Feld arrest_id Hatte keinen Index für den Fremdschlüssel. Diese Tabelle enthält 8 Indizes und 2 Fremdschlüssel. Alle anderen Felder in der Abfrage werden indiziert.

Das Feld arrest_id Hatte außer NOT NULL Keine Einschränkungen.

Die Tabelle enthält insgesamt 32 Spalten.

arrest_id Ist vom Typ Zeichen variierend (20). Ich erkenne, dass rank() einen numerischen Wert erzeugt, aber ich muss Zeichen variierend (20) verwenden, weil ich andere Zeilen habe, in denen citing_jurisdiction<>1 Nicht numerische Daten für verwendet dieses Feld.

Das Feld arrest_id War für alle Zeilen mit citing_jurisdiction=1 Leer.

Dies ist ein persönlicher High-End-Laptop (seit 1 Jahr). Ich bin der einzige Benutzer. Es wurden keine anderen Abfragen oder Vorgänge ausgeführt. Sperren scheint unwahrscheinlich.

Es gibt keine Trigger in dieser Tabelle oder anderswo in der Datenbank.

Andere Vorgänge in dieser Datenbank nehmen niemals viel Zeit in Anspruch. Bei richtiger Indizierung sind SELECT Abfragen normalerweise recht schnell.

17
Aren Cambre

Ich hatte vor kurzem etwas Ähnliches mit einer Tabelle von 3,5 Millionen Zeilen passiert. Mein Update würde niemals beendet werden. Nach vielem Experimentieren und Frust fand ich endlich den Schuldigen. Es stellte sich heraus, dass die Indizes für die Tabelle aktualisiert wurden.

Die Lösung bestand darin, alle Indizes für die zu aktualisierende Tabelle zu löschen, bevor die Update-Anweisung ausgeführt wird. Sobald ich das getan habe, war das Update in wenigen Minuten abgeschlossen. Nach Abschluss des Updates habe ich die Indizes neu erstellt und war wieder im Geschäft. Dies wird Ihnen an dieser Stelle wahrscheinlich nicht helfen, aber es kann sein, dass jemand anderes nach Antworten sucht.

Ich würde die Indizes für die Tabelle behalten, aus der Sie die Daten abrufen. Dieser muss keine Indizes ständig aktualisieren und sollte beim Auffinden der Daten helfen, die Sie aktualisieren möchten. Auf einem langsamen Laptop lief es einwandfrei.

19
JC Avena

Ihr größtes Problem besteht darin, auf einer Laptop-Festplatte sehr viel schreib- und suchintensive Arbeit zu leisten. Das wird nie schnell gehen, egal was Sie tun, besonders wenn es sich um ein langsameres 5400 U/min-Laufwerk handelt, das in vielen Laptops geliefert wird.

TrueCrypt verlangsamt das Schreiben mehr als "ein bisschen". Lesevorgänge sind relativ schnell, aber Schreibvorgänge lassen RAID 5 schnell aussehen. Das Ausführen einer Datenbank auf einem TrueCrypt-Volume ist eine Qual für Schreibvorgänge, insbesondere für zufällige Schreibvorgänge.

In diesem Fall würden Sie wahrscheinlich Ihre Zeit damit verschwenden, die Abfrage zu optimieren. Sie schreiben die meisten Zeilen sowieso neu und es wird langsam mit Ihrer schrecklichen Schreibsituation. Was ich empfehlen würde ist:

BEGIN;
SELECT ... INTO TEMPORARY TABLE master_tmp ;
TRUNCATE TABLE consistent.master;
-- Now DROP all constraints on consistent.master, then:
INSERT INTO consistent.master SELECT * FROM master_tmp;
-- ... and re-create any constraints.

Ich vermute, dass dies schneller sein wird, als nur die Einschränkungen zu löschen und neu zu erstellen, da ein UPDATE ziemlich zufällige Schreibmuster aufweist, die Ihre töten Lager. Zwei Masseneinfügungen, eine in eine nicht protokollierte Tabelle und eine in eine WAL-protokollierte Tabelle ohne Einschränkungen, werden wahrscheinlich schneller sein.

Wenn Sie absolut aktuelle Backups haben und nichts dagegen haben, Ihre Datenbank aus Backups wiederherzustellen , können Sie PostgreSQL auch mit dem fsync=off neu starten Parameter und full_page_writes=off vorübergehend für diese Massenoperation. Bei unerwarteten Problemen wie Stromausfall oder Betriebssystemabsturz kann Ihre Datenbank während fsync=off nicht wiederhergestellt werden.

Das POSTGreSQL-Äquivalent zu "keine Protokollierung" besteht darin, nicht protokollierte Tabellen zu verwenden. Diese nicht protokollierten Tabellen werden abgeschnitten, wenn die Datenbank unrein heruntergefahren wird, während sie verschmutzt sind. Die Verwendung nicht protokollierter Tabellen halbiert mindestens Ihre Schreiblast und verringert die Anzahl der Suchvorgänge, sodass sie eine MENGE schneller sein können.

Wie in Oracle kann es eine gute Idee sein, einen Index zu löschen und ihn nach einem großen Batch-Update neu zu erstellen. Der Planer von PostgreSQL kann nicht herausfinden, dass ein großes Update stattfindet. Halten Sie Indexaktualisierungen an und erstellen Sie den Index am Ende neu. Selbst wenn es möglich wäre, wäre es sehr schwer herauszufinden, an welchem ​​Punkt sich dies lohnt, insbesondere im Voraus.

15
Craig Ringer

Jemand wird eine bessere Antwort für Postgres geben, aber hier sind einige Beobachtungen aus Oracle-Sicht, die möglicherweise zutreffen (und die Kommentare sind zu lang für das Kommentarfeld).

Mein erstes Anliegen wäre es, 2 Millionen Zeilen in einer Transaktion zu aktualisieren. In Oracle würden Sie ein Vorabbild jedes zu aktualisierenden Blocks schreiben, damit andere Sitzungen weiterhin konsistent gelesen werden, ohne Ihre geänderten Blöcke zu lesen, und Sie die Möglichkeit haben, ein Rollback durchzuführen. Das ist ein langer Rollback, der aufgebaut wird. Normalerweise ist es besser, die Transaktionen in kleinen Stücken durchzuführen. Sagen Sie 1.000 Datensätze gleichzeitig.

Wenn Sie Indizes für die Tabelle haben und die Tabelle während der Wartung als außer Betrieb betrachtet wird, ist es häufig besser, die Indizes vor einer großen Operation zu entfernen und sie anschließend erneut zu erstellen. Günstiger dann ständig versuchen, die Indizes mit jedem aktualisierten Datensatz zu pflegen.

Oracle erlaubt "keine Protokollierung" Hinweise auf Anweisungen, um das Journaling zu stoppen. Es beschleunigt die Anweisungen erheblich, lässt Ihre Datenbank jedoch in einer "nicht behebbaren" Situation zurück. Sie möchten also vorher sichern und unmittelbar danach erneut sichern. Ich weiß nicht, ob Postgres ähnliche Optionen hat.

2
Glenn