sql >> Databasteknik >  >> RDS >> PostgreSQL

Postgresql Trunkeringshastighet

Detta har kommit upp några gånger nyligen, både på SO och på PostgreSQL-postlistorna.

TL;DR för dina två sista punkter:

(a) De större shared_buffers kan vara anledningen till att TRUNCATE är långsammare på CI-servern. Olika fsync-konfigurationer eller användningen av roterande media istället för SSD-enheter kan också vara felet.

(b) TRUNCATE har en fast kostnad, men inte nödvändigtvis långsammare än DELETE , plus att det fungerar mer. Se den detaljerade förklaringen som följer.

UPPDATERING: En betydande diskussion om pgsql-prestanda uppstod från detta inlägg. Se den här tråden.

UPPDATERING 2: Förbättringar har lagts till i 9.2beta3 som borde hjälpa till med detta, se detta inlägg.

Detaljerad förklaring av TRUNCATE kontra DELETE FROM :

Även om jag inte är expert på ämnet, är min uppfattning att TRUNCATE har en nästan fast kostnad per tabell, medan DELETE är åtminstone O(n) för n rader; värre om det finns några främmande nycklar som refererar till tabellen som raderas.

Jag har alltid antagit att den fasta kostnaden för en TRUNCATE var lägre än kostnaden för en DELETE på ett nästan tomt bord, men detta är inte alls sant.

TRUNCATE table; gör mer än DELETE FROM table;

Databasens tillstånd efter en TRUNCATE table är ungefär detsamma som om du istället kör:

  • DELETE FROM table;
  • VACCUUM (FULL, ANALYZE) table; (endast 9.0+, se fotnot)

... men naturligtvis TRUNCATE uppnår faktiskt inte sina effekter med en DELETE och en VACUUM .

Poängen är att DELETE och TRUNCATE gör olika saker, så att du inte bara jämför två kommandon med identiska resultat.

En DELETE FROM table; tillåter döda rader och uppsvällning att finnas kvar, låter indexen bära döda poster, uppdaterar inte tabellstatistiken som används av frågeplaneraren, etc.

En TRUNCATE ger dig en helt ny tabell och index som om de bara vore CREATE ed. Det är som att du raderade alla poster, indexerade om tabellen och gjorde en VACUUM FULL .

Om du inte bryr dig om det finns smuts kvar i tabellen eftersom du är på väg att gå och fylla den igen, kan det vara bättre att använda DELETE FROM table; .

Eftersom du inte kör VACUUM du kommer att upptäcka att döda rader och indexposter ackumuleras som uppblåsthet som måste skannas och sedan ignoreras; detta saktar ner alla dina frågor. Om dina tester inte skapar och raderar så mycket data kanske du inte märker eller bryr dig, och du kan alltid göra en VACUUM eller två halvvägs genom din testkörning om du gör det. Bättre, låt aggressiva autovakuuminställningar se till att autovacuum gör det åt dig i bakgrunden.

Du kan fortfarande TRUNCATE alla dina bord efter hela testsviten körs för att säkerställa att inga effekter byggs upp över många körningar. På 9.0 och nyare, VACUUM (FULL, ANALYZE); globalt på bordet är minst lika bra om inte bättre, och det är mycket enklare.

IIRC Pg har några optimeringar som betyder att den kanske märker när din transaktion är den enda som kan se tabellen och omedelbart markera blocken som gratis ändå. När jag i tester har velat skapa uppsvälldhet har jag behövt ha mer än en samtidig anslutning för att göra det. Jag skulle dock inte lita på det här.

DELETE FROM table; är väldigt billigt för små bord utan f/k refs

För att DELETE alla poster från en tabell utan främmande nyckelreferenser till den, måste alla Pg göra en sekventiell tabellskanning och ställa in xmax av de påträffade tuplarna. Detta är en mycket billig operation - i princip en linjär läsning och en semi-linjär skrivning. AFAIK det behöver inte röra indexen; de fortsätter att peka på de döda tuplarna tills de rensas upp av ett senare VACUUM som också markerar block i tabellen som endast innehåller döda tuplar som fria.

DELETE blir bara dyrt om det finns massor av poster, om det finns många främmande nyckelreferenser som måste kontrolleras, eller om du räknar den efterföljande VACUUM (FULL, ANALYZE) table; behövs för att matcha TRUNCATE s effekter inom kostnaden för din DELETE .

I mina tester här, en DELETE FROM table; var vanligtvis 4 gånger snabbare än TRUNCATE vid 0,5 ms mot 2 ms. Det är en test-DB på en SSD som körs med fsync=off för jag bryr mig inte om jag förlorar all denna data. Naturligtvis, DELETE FROM table; gör inte samma arbete, och om jag följer upp med en VACUUM (FULL, ANALYZE) table; det är mycket dyrare 21ms, så DELETE är bara en vinst om jag faktiskt inte behöver bordet orörda.

TRUNCATE table; gör mycket mer fast kostnadsarbete och hushållning än DELETE

Däremot en TRUNCATE måste göra mycket arbete. Den måste allokera nya filer för tabellen, dess TOAST-tabell om någon, och varje index som tabellen har. Rubriker måste skrivas in i dessa filer och systemkatalogerna kan behöva uppdateras också (osäker på den punkten, har inte kontrollerat). Den måste sedan ersätta de gamla filerna med de nya eller ta bort de gamla, och måste se till att filsystemet har kommit ikapp ändringarna med en synkroniseringsoperation - fsync() eller liknande - som vanligtvis tömmer alla buffertar till disken . Jag är inte säker på om synkroniseringen hoppas över om du kör med alternativet (dataätande) fsync=off .

Jag lärde mig nyligen att TRUNCATE måste också spola alla PostgreSQL:s buffertar relaterade till den gamla tabellen. Detta kan ta en icke-trivial tid med enorma shared_buffers . Jag misstänker att det är därför det är långsammare på din CI-server.

Saldo

Hur som helst, du kan se att en TRUNCATE av en tabell som har en associerad TOAST-tabell (de flesta gör det) och flera index kan ta en stund. Inte lång, men längre än en DELETE från ett nästan tomt bord.

Följaktligen kan det vara bättre att du gör en DELETE FROM table; .

--

Obs:på DB:er före 9.0, CLUSTER table_id_seq ON table; ANALYZE table; eller VACUUM FULL ANALYZE table; REINDEX table; skulle vara en närmare motsvarighet till TRUNCATE . VACUUM FULL impl ändrades till en mycket bättre i 9.0.



  1. Hur skapar jag en tillfällig tabell i SQL?

  2. Välj första raden i varje GROUP BY-grupp?

  3. orakel - vilka uttalanden måste göras?

  4. Hur DATE_SUB() fungerar i MariaDB