Dies ist in letzter Zeit einige Male aufgetaucht, sowohl auf SO als auch auf den PostgreSQL-Mailinglisten.
Der TL;DR für Ihre letzten beiden Punkte:
(a) Die größeren shared_buffers können der Grund dafür sein, dass TRUNCATE auf dem CI-Server langsamer ist. Eine andere fsync-Konfiguration oder die Verwendung von Rotationsmedien anstelle von SSDs könnten ebenfalls schuld sein.
(b) TRUNCATE
hat feste Kosten, ist aber nicht unbedingt langsamer als DELETE
, außerdem macht es mehr Arbeit. Siehe die detaillierte Erklärung, die folgt.
AKTUALISIERUNG: Aus diesem Beitrag entstand eine bedeutende Diskussion über die Leistung von pgsql. Siehe diesen Thread.
AKTUALISIERUNG 2: Verbesserungen wurden zu 9.2beta3 hinzugefügt, die dabei helfen sollten, siehe diesen Beitrag.
Detaillierte Erklärung von TRUNCATE
vs DELETE FROM
:
Obwohl ich kein Experte auf diesem Gebiet bin, verstehe ich, dass TRUNCATE
hat fast feste Kosten pro Tisch, während DELETE
für n Zeilen mindestens O(n) ist; schlimmer, wenn es Fremdschlüssel gibt, die auf die zu löschende Tabelle verweisen.
Ich bin immer davon ausgegangen, dass die Fixkosten für ein TRUNCATE
liegen war niedriger als die Kosten für ein DELETE
auf einem fast leeren Tisch, aber das stimmt überhaupt nicht.
TRUNCATE table;
macht mehr als DELETE FROM table;
Der Zustand der Datenbank nach einer TRUNCATE table
ist ziemlich dasselbe, als ob Sie stattdessen Folgendes ausführen würden:
DELETE FROM table;
VACCUUM (FULL, ANALYZE) table;
(nur 9.0+, siehe Fußnote)
... aber natürlich TRUNCATE
erzielt mit einem DELETE
nicht wirklich seine Wirkung und ein VACUUM
.
Der Punkt ist, dass DELETE
und TRUNCATE
verschiedene Dinge tun, sodass Sie nicht nur zwei Befehle mit identischen Ergebnissen vergleichen.
Eine DELETE FROM table;
lässt zu, dass tote Zeilen und Aufblähen verbleiben, lässt zu, dass die Indizes tote Einträge enthalten, aktualisiert die vom Abfrageplaner verwendeten Tabellenstatistiken nicht usw.
Ein TRUNCATE
gibt Ihnen eine komplett neue Tabelle und Indizes, als ob sie nur CREATE
wären ed. Es ist, als hätten Sie alle Datensätze gelöscht, die Tabelle neu indiziert und ein VACUUM FULL
durchgeführt .
Wenn es Ihnen egal ist, ob noch Reste in der Tabelle sind, weil Sie sie gleich wieder auffüllen wollen, ist es vielleicht besser, wenn Sie DELETE FROM table;
verwenden .
Weil Sie VACUUM
nicht ausführen Sie werden feststellen, dass sich tote Zeilen und Indexeinträge als aufgebläht ansammeln, die gescannt und dann ignoriert werden müssen; Dies verlangsamt alle Ihre Abfragen. Wenn Ihre Tests nicht wirklich so viele Daten erstellen und löschen, bemerken Sie es vielleicht nicht oder kümmern sich nicht darum, und Sie können immer ein VACUUM
durchführen oder zwei auf halbem Weg durch Ihren Testlauf, wenn Sie dies tun. Besser, lassen Sie aggressive Autovacuum-Einstellungen sicherstellen, dass Autovacuum dies für Sie im Hintergrund erledigt.
Sie können immer noch TRUNCATE
alle Ihre Tabellen nach dem Ganzen Testsuite läuft, um sicherzustellen, dass sich über viele Läufe keine Effekte aufbauen. Ab Version 9.0 VACUUM (FULL, ANALYZE);
global auf dem Tisch ist mindestens genauso gut, wenn nicht sogar besser, und es ist viel einfacher.
IIRC Pg hat einige Optimierungen, die bedeuten, dass es bemerken könnte, wenn Ihre Transaktion die einzige ist, die die Tabelle sehen kann, und die Blöcke trotzdem sofort als frei markiert. Wenn ich beim Testen Aufblähen erzeugen wollte, musste ich mehr als eine gleichzeitige Verbindung haben, um dies zu tun. Darauf würde ich mich aber nicht verlassen.
DELETE FROM table;
ist sehr günstig für kleine Tische ohne f/k-Referenzen
Zum DELETE
alle Datensätze aus einer Tabelle ohne Fremdschlüsselverweise darauf, alle Pg müssen einen sequentiellen Tabellenscan durchführen und den xmax
setzen der gefundenen Tupel. Dies ist eine sehr billige Operation – im Grunde ein lineares Lesen und ein halblineares Schreiben. AFAIK muss die Indizes nicht berühren; sie zeigen weiterhin auf die toten Tupel, bis sie durch ein späteres VACUUM
bereinigt werden das markiert auch Blöcke in der Tabelle, die nur tote Tupel enthalten, als frei.
DELETE
wird nur teuer, wenn es viele gibt von Datensätzen, wenn es viele Fremdschlüsselreferenzen gibt, die überprüft werden müssen, oder wenn Sie die nachfolgende VACUUM (FULL, ANALYZE) table;
benötigt, um TRUNCATE
abzugleichen 's Effekte innerhalb der Kosten Ihres DELETE
.
In meinen Tests hier eine DELETE FROM table;
war typischerweise 4x schneller als TRUNCATE
bei 0,5 ms gegenüber 2 ms. Das ist eine Test-DB auf einer SSD, die mit fsync=off
läuft weil es mir egal ist, ob ich all diese Daten verliere. Natürlich DELETE FROM table;
nicht die gleiche Arbeit macht, und wenn ich mit einer VACUUM (FULL, ANALYZE) table;
weitermache es sind viel teurere 21 ms, also das DELETE
ist nur ein Gewinn, wenn ich den Tisch nicht unbedingt makellos brauche.
TRUNCATE table;
macht viel mehr Fixkostenarbeit und Haushalt als DELETE
Im Gegensatz dazu ein TRUNCATE
muss viel arbeiten. Es muss neue Dateien für die Tabelle, seine TOAST-Tabelle, falls vorhanden, und jeden Index, den die Tabelle hat, zuweisen. Header müssen in diese Dateien geschrieben werden, und die Systemkataloge müssen möglicherweise ebenfalls aktualisiert werden (in diesem Punkt nicht sicher, habe es nicht überprüft). Es muss dann die alten Dateien durch die neuen ersetzen oder die alten entfernen und sicherstellen, dass das Dateisystem die Änderungen mit einer Synchronisierungsoperation - fsync() oder ähnlichem - eingeholt hat, die normalerweise alle Puffer auf die Festplatte löscht . Ich bin mir nicht sicher, ob die Synchronisierung übersprungen wird, wenn Sie mit der (datenfressenden) Option fsync=off
laufen .
Ich habe kürzlich gelernt, dass TRUNCATE
muss auch alle PostgreSQL-Puffer leeren, die sich auf die alte Tabelle beziehen. Dies kann bei riesigen shared_buffers
eine nicht triviale Zeit in Anspruch nehmen . Ich vermute, das ist der Grund, warum es auf Ihrem CI-Server langsamer ist.
Das Gleichgewicht
Wie auch immer, Sie können das als TRUNCATE
sehen einer Tabelle, die eine zugeordnete TOAST-Tabelle (die meisten) und mehrere Indizes hat, kann einen Moment dauern. Nicht lang, aber länger als ein DELETE
von einem fast leeren Tisch.
Folglich sollten Sie besser ein DELETE FROM table;
ausführen .
--
Hinweis:Auf DBs vor 9.0, CLUSTER table_id_seq ON table; ANALYZE table;
oder VACUUM FULL ANALYZE table; REINDEX table;
wäre ein genaueres Äquivalent zu TRUNCATE
. Der VACUUM FULL
impl wurde in 9.0 zu einem viel besseren geändert.