Ci sono 2 parti alla mia domanda.
- Esiste un modo per specificare la dimensione iniziale di un database in PostgreSQL?
- In caso contrario, come gestisci la frammentazione quando il database cresce nel tempo?
Di recente sono migrato da MSSQL a Postgres e una delle cose che abbiamo fatto nel mondo MSSQL durante la creazione di un database è stata quella di specificare la dimensione iniziale del database e del registro delle transazioni. Ciò ha ridotto la frammentazione e aumentato le prestazioni, soprattutto se si conosce in anticipo la dimensione "normale" del database.
Le prestazioni del mio database diminuiscono con l'aumentare delle dimensioni. Ad esempio, il carico di lavoro che sto attraversando richiede normalmente 10 minuti. Man mano che il database cresce, questa volta aumenta. Fare un VUOTO, VUOTO PIENO e VUOTO PIENO ANALISI non sembra risolvere il problema. Ciò che risolve il problema delle prestazioni è l'arresto del database, la frammentazione del disco e quindi l'esecuzione di un ANALIZZO COMPLETO VACUUM riporta le prestazioni del mio test ai 10 minuti originali. Questo mi porta a sospettare che la frammentazione sia ciò che mi sta causando dolore.
Non sono stato in grado di trovare alcun riferimento alla prenotazione di tablespace / spazio del database in Postgres. O sto usando una terminologia sbagliata e quindi non trovo nulla, oppure esiste un modo diverso di mitigare la frammentazione del filesystem in Postgres.
Qualche puntatore?
La soluzione
Le risposte fornite mi hanno aiutato a confermare ciò che avevo iniziato a sospettare. PostgreSQL memorizza il database su più file e questo è ciò che consente al database di crescere senza preoccuparsi della frammentazione. Il comportamento predefinito è comprimere questi file fino all'orlo con i dati della tabella, il che è utile per le tabelle che cambiano raramente, ma è dannoso per le tabelle che vengono aggiornate frequentemente.
PostgreSQL utilizza MVCC per fornire accesso simultaneo ai dati della tabella. In base a questo schema, ogni aggiornamento crea una nuova versione della riga che è stata aggiornata (potrebbe essere tramite timestamp o numero di versione, chi lo sa?). I vecchi dati non vengono immediatamente eliminati, ma contrassegnati per l'eliminazione. La cancellazione effettiva si verifica quando viene eseguita un'operazione VACUUM.
Come si collega questo al fattore di riempimento? Il fattore di riempimento predefinito di 100 della tabella comprime completamente le pagine della tabella, il che a sua volta significa che non vi è spazio all'interno della pagina della tabella per contenere le righe aggiornate, ovvero le righe aggiornate verranno posizionate in una pagina della tabella diversa dalla riga originale. Questo è negativo per le prestazioni, come dimostra la mia esperienza. Poiché le mie tabelle di riepilogo vengono aggiornate molto frequentemente (fino a 1500 righe / sec), ho scelto di impostare un fattore di riempimento di 20, ovvero il 20% della tabella sarà per i dati delle righe inseriti e l'80% per i dati di aggiornamento. Sebbene ciò possa sembrare eccessivo, la grande quantità di spazio riservato alle righe aggiornate significa che le righe aggiornate rimangono nella stessa pagina dell'originale e c'è una pagina della tabella non piena quando il daemon autovacuum viene eseguito per rimuovere le righe obsolete.
Per "riparare" il mio database, ho fatto quanto segue.
- Imposta il fattore di riempimento delle mie tabelle di riepilogo su 20. Puoi farlo al momento della creazione passando un parametro su CREATE TABLE o dopo il fatto tramite ALTER TABLE. Ho emesso il seguente comando plpgsql:
ALTER TABLE "my_summary_table" SET (fillfactor = 20);
- Emesso un VACUUM FULL, in quanto questo scrive una versione completamente nuova del file della tabella e quindi, implicitamente, scrive un nuovo file della tabella con il nuovo fattore di riempimento .
Rieseguendo i miei test, non vedo alcun peggioramento delle prestazioni anche quando il database è grande quanto ho bisogno che lo sia con molti milioni di righe.
TL; DR - La frammentazione dei file non era la causa, era la frammentazione del tablespace. Ciò viene mitigato modificando il fattore di riempimento della tabella per adattarlo al proprio caso d'uso.