Ich habe eine Postgres-Tabelle mit ~ 2,1 Millionen Zeilen. Ich habe das folgende Update durchgefü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) Kern i7 Q720 Laptop-Prozessor, viel RAM, sonst läuft die überwiegende Mehrheit der Zeit nichts. Keine Festplattenspeicherbeschränkungen. Die Tabelle wurde kürzlich gesaugt, analysiert und neu indiziert.
Während der gesamten Zeit, in der die Abfrage ausgeführt wurde, war die WITH
CPU-Auslastung in der Regel niedrig und die Festplatte war zu 100% belegt. Die Festplatte war so stark ausgelastet, dass jede andere App deutlich langsamer lief als normal.
Die Leistungseinstellung des Laptops war Hochleistung (Windows 7 x 64).
Hier ist der EXPLAIN:
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. Trotz dieser WHERE
Klausel arbeite ich immer noch mit über 2 Millionen Zeilen.
Die Festplatte ist vollständig mit TrueCrypt 7.1a verschlüsselt. Dass verlangsamen Dinge ein wenig nach unten, aber nicht genug , um eine Abfrage zu veranlassen , zu nehmen , dass viele Stunden.
Die WITH
Ausführung des Teils dauert nur ca. 3 Minuten.
Das arrest_id
Feld hatte keinen Index für Fremdschlüssel. Diese Tabelle enthält 8 Indizes und 2 Fremdschlüssel. Alle anderen Felder in der Abfrage werden indiziert.
Das arrest_id
Feld hatte keine Einschränkungen außer NOT NULL
.
Die Tabelle enthält insgesamt 32 Spalten.
arrest_id
ist vom Typ Zeichen variiert (20) . Mir ist klar, dass rank()
ein numerischer Wert erzeugt wird, aber ich muss Zeichenvariation (20) verwenden, weil ich andere Zeilen habe, in citing_jurisdiction<>1
denen nicht numerische Daten für dieses Feld verwendet werden.
Das arrest_id
Feld war für alle Zeilen mit leer citing_jurisdiction=1
.
Dies ist ein persönlicher High-End-Laptop (Stand vor 1 Jahr). Ich bin der einzige Benutzer. Es wurden keine anderen Abfragen oder Vorgänge ausgeführt. Das Sperren scheint unwahrscheinlich.
Es gibt keine Trigger in dieser Tabelle oder irgendwo anders in der Datenbank.
Andere Vorgänge in dieser Datenbank benötigen nie sonderlich viel Zeit. Bei richtiger Indizierung sind SELECT
Abfragen normalerweise recht schnell.
quelle
Seq Scan
sind ein bisschen beängstigend ...Antworten:
Ich hatte vor kurzem etwas Ähnliches mit einer Tabelle von 3,5 Millionen Zeilen passiert. Mein Update würde niemals fertig werden. Nach vielem Experimentieren und Frust habe ich endlich den Täter gefunden. Es stellte sich heraus, dass die Indizes für die Tabelle aktualisiert wurden.
Die Lösung bestand darin, alle Indizes in der zu aktualisierenden Tabelle zu löschen, bevor die Aktualisierungsanweisung ausgeführt wurde. Sobald ich das getan habe, ist 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 weiterhelfen, aber es könnte jemand anders sein, der nach Antworten sucht.
Ich würde die Indizes auf dem Tisch belassen, von dem Sie die Daten abrufen. Dieser muss keine Indizes mehr aktualisieren und sollte bei der Suche nach den zu aktualisierenden Daten hilfreich sein. Es lief gut auf einem langsamen Laptop.
quelle
Ihr größtes Problem besteht darin, große Mengen schreib- und suchintensiver Arbeit auf einer Laptop-Festplatte zu verrichten. Das wird nie schnell, egal was Sie tun, besonders wenn es sich um ein langsameres Laufwerk mit 5400 U / min handelt, das in vielen Laptops geliefert wird.
TrueCrypt verlangsamt die Schreibvorgänge mehr als "ein bisschen". Lesevorgänge sind relativ schnell, Schreibvorgänge lassen RAID 5 jedoch 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 sowieso die meisten Zeilen neu und es wird langsam mit Ihrer schrecklichen Schreibsituation. Was ich empfehlen würde, ist:
Ich vermute, dass dies schneller geht, als nur die Einschränkungen zu löschen und neu zu erstellen, da ein UPDATE ziemlich zufällige Schreibmuster enthält, die Ihren Speicherplatz zerstören. Zwei Masseneinfügungen, eine in eine nicht protokollierte Tabelle und eine in eine WAL-protokollierte Tabelle ohne Einschränkungen, sind wahrscheinlich schneller.
Wenn Sie über absolut aktuelle Sicherungen verfügen und es Ihnen nichts ausmacht, Ihre Datenbank aus Sicherungen wiederherzustellen, können Sie PostgreSQL auch mit dem
fsync=off
Parameter undfull_page_writes=off
vorübergehend für diese Massenoperation neu starten . Jedes unerwartete Problem wie ein Stromausfall oder ein Betriebssystemabsturz lässt Ihre Datenbank währenddessen nicht wiederherstellbarfsync=off
.Das POSTGreSQL-Äquivalent zu "keine Protokollierung" besteht darin, nicht protokollierte Tabellen zu verwenden. Diese nicht protokollierten Tabellen werden abgeschnitten, wenn die Datenbank unsauber heruntergefahren wird, während sie fehlerhaft sind. Wenn Sie nicht protokollierte Tabellen verwenden, halbieren Sie mindestens Ihre Schreiblast und reduzieren die Anzahl der Suchvorgänge, sodass diese VIEL schneller ausgeführt werden können.
Wie in Oracle kann es eine gute Idee sein, einen Index zu löschen und nach einem großen Batch-Update neu zu erstellen. Der Planer von PostgreSQL kann nicht feststellen, dass ein großes Update stattfindet. Halten Sie die Indexaktualisierungen an und erstellen Sie den Index am Ende neu. Selbst wenn es möglich wäre, wäre es sehr schwierig, herauszufinden, an welchem Punkt sich dies lohnt, besonders im Voraus.
quelle
HOT
Sie die Indizes an Ort und Stelle belassen. Wenn nicht, möchten Sie wahrscheinlich löschen und neu erstellen. Die Spalte ist nicht indiziert, aber um ein HOT-Update durchführen zu können, muss auf derselben Seite auch freier Speicherplatz vorhanden sein. Dies hängt also ein wenig davon ab, wie viel Totraum in der Tabelle vorhanden ist. Wenn es hauptsächlich schreibend ist, würde ich sagen, dass alle Indizes gelöscht werden. Wenn es Lose aktualisiert hat, hat es möglicherweise Löcher und Sie sind möglicherweise in Ordnung. Tools wiepageinspect
undpg_freespacemap
können dabei helfen, dies festzustellen.Jemand wird eine bessere Antwort für Postgres geben, aber hier sind einige Beobachtungen aus einer Oracle-Perspektive, die zutreffen könnten (und die Kommentare sind zu lang für das Kommentarfeld).
Meine erste Sorge wäre, zu versuchen, 2 Millionen Zeilen in einer Transaktion zu aktualisieren. In Oracle schreiben Sie ein Vorabbild jedes zu aktualisierenden Blocks, sodass in einer anderen Sitzung immer noch ein konsistenter Lesevorgang stattfindet, ohne die geänderten Blöcke zu lesen, und Sie haben die Möglichkeit, ein Rollback durchzuführen. Das ist ein langer Rollback. Sie sind normalerweise besser dran, 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. Billiger ist es dann, ständig zu versuchen, die Indizes mit jedem aktualisierten Datensatz zu pflegen.
Oracle lässt Hinweise auf Anweisungen ohne Protokollierung zu, um das Journalling zu stoppen. Dadurch werden die Anweisungen erheblich beschleunigt, aber Ihre Datenbank befindet sich in einer "nicht behebbaren" Situation. Sie möchten also vorher und unmittelbar danach erneut sichern. Ich weiß nicht, ob Postgres ähnliche Optionen hat.
quelle
VACUUM
nur die halbe Antwort ist; PostgreSQL verwendet auch ein sogenanntes "Write Ahead Log" ( quasi