Dit is de laatste tijd een paar keer naar voren gekomen, zowel op SO als op de PostgreSQL-mailinglijsten.
De TL;DR voor je laatste twee punten:
(a) De grotere shared_buffers kunnen de reden zijn waarom TRUNCATE langzamer is op de CI-server. Een andere fsync-configuratie of het gebruik van roterende media in plaats van SSD's kan ook de oorzaak zijn.
(b) TRUNCATE
heeft vaste kosten, maar niet noodzakelijk langzamer dan DELETE
, plus het doet meer werk. Zie de gedetailleerde uitleg die volgt.
UPDATE: Uit dit bericht ontstond een belangrijke discussie over pgsql-prestaties. Zie dit draadje.
UPDATE 2: Er zijn verbeteringen toegevoegd aan 9.2beta3 die hierbij zouden moeten helpen, zie dit bericht.
Gedetailleerde uitleg van TRUNCATE
vs DELETE FROM
:
Hoewel ik geen expert ben op dit gebied, begrijp ik dat TRUNCATE
heeft bijna vaste kosten per tafel, terwijl DELETE
is ten minste O(n) voor n rijen; erger als er externe sleutels zijn die verwijzen naar de tabel die wordt verwijderd.
Ik ging er altijd vanuit dat de vaste kosten van een TRUNCATE
was lager dan de kosten van een DELETE
op een bijna lege tafel, maar dit is helemaal niet waar.
TRUNCATE table;
doet meer dan DELETE FROM table;
De status van de database na een TRUNCATE table
is ongeveer hetzelfde als wanneer u in plaats daarvan zou rennen:
DELETE FROM table;
VACCUUM (FULL, ANALYZE) table;
(alleen 9.0+, zie voetnoot)
... hoewel natuurlijk TRUNCATE
bereikt zijn effecten niet echt met een DELETE
en een VACUUM
.
Het punt is dat DELETE
en TRUNCATE
verschillende dingen doen, dus je vergelijkt niet zomaar twee commando's met identieke resultaten.
Een DELETE FROM table;
laat dode rijen en bloat toe, staat toe dat de indexen dode items bevatten, werkt de tabelstatistieken die door de queryplanner worden gebruikt niet bij, enz.
Een TRUNCATE
geeft je een compleet nieuwe tabel en indexen alsof ze gewoon CREATE
. zijn red. Het is alsof je alle records hebt verwijderd, de tabel opnieuw hebt geïndexeerd en een VACUUM FULL
hebt gedaan .
Als het je niet uitmaakt of er nog crud in de tabel zit omdat je op het punt staat om hem weer te gaan vullen, kun je beter DELETE FROM table;
gebruiken. .
Omdat je VACUUM
. niet gebruikt u zult merken dat dode rijen en indexitems zich ophopen als een opgeblazen gevoel dat moet worden gescand en vervolgens moet worden genegeerd; dit vertraagt al uw vragen. Als uw tests niet echt zoveel gegevens creëren en verwijderen, merkt u het misschien niet of geeft u er niets om, en u kunt altijd een VACUUM
doen of twee halverwege uw testrun als u dat doet. Beter, laat agressieve autovacuum-instellingen ervoor zorgen dat autovacuum het op de achtergrond voor je doet.
Je kunt nog steeds TRUNCATE
al je tafels na de hele testsuite-runs om ervoor te zorgen dat er geen effecten worden opgebouwd over veel runs. Op 9.0 en nieuwer, VACUUM (FULL, ANALYZE);
wereldwijd op tafel is minstens zo goed, zo niet beter, en het is een stuk eenvoudiger.
IIRC Pg heeft een paar optimalisaties waardoor het kan opvallen wanneer uw transactie de enige is die de tabel kan zien en de blokken toch meteen als gratis markeert. Bij het testen, toen ik een bloat wilde creëren, moest ik meer dan één gelijktijdige verbinding hebben om het te doen. Ik zou hier echter niet op vertrouwen.
DELETE FROM table;
is erg goedkoop voor kleine tafels zonder f/k refs
Om DELETE
alle records uit een tabel zonder refererende sleutelverwijzingen ernaar, alle Pg moet een sequentiële tabelscan uitvoeren en de xmax
instellen van de tegengekomen tuples. Dit is een zeer goedkope operatie - in feite een lineair lezen en een semi-lineair schrijven. AFAIK het hoeft de indexen niet te raken; ze blijven naar de dode tupels wijzen totdat ze worden opgeruimd door een latere VACUUM
dat markeert ook blokken in de tabel die alleen dode tuples bevatten als gratis.
DELETE
wordt alleen duur als er loten zijn van records, als er veel refererende sleutelreferenties zijn die gecontroleerd moeten worden, of als u de volgende VACUUM (FULL, ANALYZE) table;
nodig om overeen te komen met TRUNCATE
's effecten binnen de kosten van uw DELETE
.
In mijn tests hier, een DELETE FROM table;
was doorgaans 4x sneller dan TRUNCATE
bij 0,5 ms versus 2 ms. Dat is een test-DB op een SSD, draaiend met fsync=off
omdat het me niet kan schelen als ik al deze gegevens verlies. Natuurlijk, DELETE FROM table;
niet allemaal hetzelfde werk doet, en als ik volg met een VACUUM (FULL, ANALYZE) table;
het is een veel duurdere 21ms, dus de DELETE
is alleen een overwinning als ik de tafel niet echt nodig heb.
TRUNCATE table;
doet veel meer vast werk en huishouden dan DELETE
Daarentegen een TRUNCATE
veel werk moet doen. Het moet nieuwe bestanden toewijzen aan de tabel, de TOAST-tabel, indien aanwezig, en elke index die de tabel heeft. Headers moeten in die bestanden worden geschreven en de systeemcatalogi moeten mogelijk ook worden bijgewerkt (ik weet het niet zeker, heb het niet gecontroleerd). Het moet dan de oude bestanden vervangen door de nieuwe of de oude verwijderen, en moet ervoor zorgen dat het bestandssysteem de wijzigingen heeft ingehaald met een synchronisatiebewerking - fsync() of iets dergelijks - dat gewoonlijk alle buffers naar de schijf leegt . Ik weet niet zeker of de synchronisatie wordt overgeslagen als je de (data-etende) optie fsync=off
gebruikt .
Ik heb onlangs geleerd dat TRUNCATE
moet ook alle PostgreSQL-buffers leegmaken die betrekking hebben op de oude tabel. Dit kan een niet-triviale hoeveelheid tijd kosten met enorme shared_buffers
. Ik vermoed dat dit de reden is waarom het langzamer is op je CI-server.
Het saldo
Hoe dan ook, je kunt zien dat een TRUNCATE
van een tabel met een bijbehorende TOAST-tabel (de meeste doen dat) en verschillende indexen kan even duren. Niet lang, maar langer dan een DELETE
van een bijna lege tafel.
Daarom is het misschien beter om een DELETE FROM table;
.
--
Opmerking:op DB's vóór 9.0, CLUSTER table_id_seq ON table; ANALYZE table;
of VACUUM FULL ANALYZE table; REINDEX table;
zou een beter equivalent zijn van TRUNCATE
. De VACUUM FULL
impl veranderd in een veel betere in 9.0.