PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

Velocità di troncamento Postgresql

Questo è emerso un paio di volte di recente, sia su SO che sulle mailing list di PostgreSQL.

Il TL;DR per i tuoi ultimi due punti:

(a) I buffer condivisi più grandi potrebbero essere il motivo per cui TRUNCATE è più lento sul server CI. Anche la diversa configurazione di fsync o l'uso di supporti rotazionali al posto degli SSD potrebbero essere in errore.

(b) TRUNCATE ha un costo fisso, ma non necessariamente più lento di DELETE , inoltre fa più lavoro. Vedi la spiegazione dettagliata che segue.

AGGIORNAMENTO: Da questo post è nata una discussione significativa su pgsql-performance. Vedi questo thread.

AGGIORNAMENTO 2: Sono stati aggiunti miglioramenti a 9.2beta3 che dovrebbero aiutare in questo, vedere questo post.

Spiegazione dettagliata di TRUNCATE vs DELETE FROM :

Pur non essendo un esperto dell'argomento, la mia comprensione è che TRUNCATE ha un costo per tavolo quasi fisso, mentre DELETE è almeno O(n) per n righe; peggio se ci sono chiavi esterne che fanno riferimento alla tabella che viene eliminata.

Ho sempre pensato che il costo fisso di un TRUNCATE era inferiore al costo di un DELETE su un tavolo quasi vuoto, ma questo non è affatto vero.

TRUNCATE table; fa più di DELETE FROM table;

Lo stato del database dopo una TRUNCATE table è più o meno come se eseguissi invece:

  • DELETE FROM table;
  • VACCUUM (FULL, ANALYZE) table; (solo 9.0+, vedi nota a piè di pagina)

... anche se ovviamente TRUNCATE in realtà non ottiene i suoi effetti con un DELETE e un VACUUM .

Il punto è che DELETE e TRUNCATE fare cose diverse, quindi non stai solo confrontando due comandi con risultati identici.

Una tabella DELETE FROM table; consente a righe morte e bloat di rimanere, consente agli indici di trasportare voci morte, non aggiorna le statistiche della tabella utilizzate dal pianificatore di query, ecc.

Un TRUNCATE ti dà una tabella completamente nuova e indici come se fossero solo CREATE ed. È come se avessi cancellato tutti i record, reindicizzato la tabella e fatto un VACUUM FULL .

Se non ti interessa se nella tabella è rimasto del crud perché stai per andare a riempirlo di nuovo, potresti fare meglio a usare DELETE FROM table; .

Perché non stai eseguendo VACUUM scoprirai che le righe morte e le voci di indice si accumulano come rigonfiamenti che devono essere scansionati e poi ignorati; questo rallenta tutte le tue query. Se i tuoi test in realtà non creano ed eliminano tutti quei dati che potresti non notare o non preoccuparti, e puoi sempre fare un VACUUM o due a metà del test, se lo fai. Meglio, lascia che le impostazioni aggressive dell'autovacuum garantiscano che l'autovacuum lo faccia per te in background.

Puoi ancora TRUNCATE tutti i tuoi tavoli dopo il intero test suite viene eseguito per assicurarsi che nessun effetto si accumuli su molte esecuzioni. Nella versione 9.0 e successive, VACUUM (FULL, ANALYZE) table; a livello globale sul tavolo è almeno altrettanto buono se non migliore, ed è molto più semplice.

IIRC Pg ha alcune ottimizzazioni che significano che potrebbe notare quando la tua transazione è l'unica in grado di vedere la tabella e contrassegnare immediatamente i blocchi come liberi comunque. Durante i test, quando ho voluto creare un rigonfiamento, ho dovuto avere più di una connessione simultanea per farlo. Non farei affidamento su questo, però.

DELETE FROM table; è molto economico per tavoli piccoli senza riferimenti f/k

Per DELETE tutti i record da una tabella senza riferimenti a chiave esterna, tutto Pg deve eseguire una scansione sequenziale della tabella e impostare xmax delle tuple incontrate. Questa è un'operazione molto economica:fondamentalmente una lettura lineare e una scrittura semilineare. AFAIK non deve toccare gli indici; continuano a puntare alle tuple morte finché non vengono ripulite da un successivo VACUUM che contrassegna anche i blocchi nella tabella contenenti solo tuple morte come libere.

DELETE diventa costoso solo se ci sono lotti di record, se ci sono molti riferimenti di chiave esterna che devono essere controllati, o se si conta la successiva tabella VACUUM (FULL, ANALYZE) table; necessario per abbinare TRUNCATE 's effetti entro il costo del tuo DELETE .

Nei miei test qui, una tabella DELETE FROM table; era in genere 4 volte più veloce di TRUNCATE a 0,5 ms contro 2 ms. Questo è un DB di prova su un SSD, in esecuzione con fsync=off perché non mi interessa se perdo tutti questi dati. Naturalmente, DELETE FROM table; non sta facendo lo stesso lavoro e se continuo con una tabella VACUUM (FULL, ANALYZE) table; sono 21ms molto più costosi, quindi DELETE è solo una vittoria se non ho davvero bisogno del tavolo incontaminato.

TRUNCATE table; fa molto più lavoro e pulizia a costi fissi rispetto a DELETE

Al contrario, un TRUNCATE deve fare molto lavoro. Deve allocare nuovi file per la tabella, la relativa tabella TOAST, se presente, e ogni indice della tabella. Le intestazioni devono essere scritte in quei file e potrebbe essere necessario aggiornare anche i cataloghi di sistema (non sono sicuro su questo punto, non ho controllato). Quindi deve sostituire i vecchi file con quelli nuovi o rimuovere quelli vecchi e deve assicurarsi che il file system abbia raggiunto le modifiche con un'operazione di sincronizzazione - fsync() o simile - che di solito scarica tutti i buffer sul disco . Non sono sicuro che la sincronizzazione venga saltata se stai utilizzando l'opzione (mangia dati) fsync=off .

Recentemente ho appreso che TRUNCATE deve anche svuotare tutti i buffer di PostgreSQL relativi alla vecchia tabella. Questo può richiedere una quantità di tempo non banale con enormi shared_buffers . Sospetto che questo sia il motivo per cui è più lento sul tuo server CI.

L'equilibrio

Ad ogni modo, puoi vedere che un TRUNCATE di una tabella a cui è associata una tabella TOAST (la maggior parte lo fa) e diversi indici potrebbero richiedere alcuni istanti. Non lungo, ma più lungo di un DELETE da un tavolo quasi vuoto.

Di conseguenza, potresti fare meglio a fare una tabella DELETE FROM table; .

--

Nota:su DB precedenti alla 9.0, CLUSTER table_id_seq ON table; ANALYZE table; o tabella VACUUM FULL ANALYZE table; REINDEX table; sarebbe un equivalente più vicino a TRUNCATE . Il VACUUM FULL impl è cambiato in uno molto migliore nella 9.0.