PostgreSQL è uno dei database open source più popolari al mondo e ha implementazioni di successo in diversi ambienti mission-critical in vari domini, utilizzando applicazioni OLTP di fascia alta in tempo reale che eseguono milioni e miliardi di transazioni al giorno. PostgreSQL I/O è abbastanza affidabile, stabile e performante praticamente su qualsiasi hardware, incluso anche il cloud.
Per garantire che i database funzionino alla scala prevista con i tempi di risposta previsti, è necessaria una progettazione delle prestazioni. Bene, il raggiungimento di buone prestazioni del database dipende da vari fattori. Le prestazioni del database possono peggiorare per vari motivi, ad esempio il dimensionamento dell'infrastruttura, la strategia di manutenzione del database inefficiente, il codice SQL scadente o i processi del database mal configurati che non utilizzano tutte le risorse disponibili:CPU, memoria, larghezza di banda di rete e I/O del disco.
Cosa può causare un peggioramento delle prestazioni del database?
- Query scritte male con join, logica ecc. errati che richiedono molta CPU e memoria
- Query che eseguono scansioni di tabelle complete su tavoli di grandi dimensioni a causa di un'indicizzazione impropria
- Manutenzione del database errata senza statistiche corrette in atto
- Pianificazione inefficiente della capacità con conseguente infrastruttura di dimensioni inadeguate
- Progettazione logica e fisica impropria
- Nessun pool di connessioni in atto, che fa sì che le applicazioni effettuino un numero enorme di connessioni in modo incontrollabile
Quindi ci sono molte potenziali aree che possono causare problemi di prestazioni. Una delle aree significative su cui vorrei concentrarmi in questo blog è come ottimizzare le prestazioni di I/O (Input/Output) di PostgreSQL. L'ottimizzazione delle operazioni di Input/Output di PostgreSQL è essenziale, specialmente in un ambiente ad alta transazionalità come OLTP o in un ambiente di data warehousing con analisi di dati complesse su set di dati di grandi dimensioni.
Nella maggior parte dei casi, i problemi di prestazioni del database sono causati principalmente da I/O elevati. Ciò significa che i processi di database trascorrono più tempo a scrivere o leggere dal disco. Qualsiasi operazione sui dati in tempo reale è vincolata all'I/O, è fondamentale assicurarsi che il database sia ottimizzato per l'I/O. In questo blog mi concentrerò sui problemi di I/O comuni che i database PostgreSQL possono incontrare negli ambienti di produzione in tempo reale.
Ottimizzazione dell'I/O di PostgreSQL
L'ottimizzazione dell'I/O di PostgreSQL è fondamentale per la creazione di un'architettura di database scalabile e altamente performante. Esaminiamo vari fattori che influiscono sulle prestazioni di I/O:
- Indicizzazione
- Partizionamento
- Punti di controllo
- VUOTO, ANALIZZA (con FILLFACTOR)
- Altri problemi di I/O
- I/O PostgreSQL su Cloud
- Strumenti
Indicizzazione
L'indicizzazione è una delle principali tecniche di ottimizzazione che svolge un ruolo fondamentale nel miglioramento delle prestazioni di I/O del database. Questo vale davvero per qualsiasi database. PostgreSQL supporta vari tipi di indice che possono accelerare notevolmente le operazioni di lettura, offrendo una maggiore scalabilità per le applicazioni. Sebbene la creazione di indici sia abbastanza semplice e diretta, è essenziale che i DBA e gli sviluppatori sappiano quale tipo di indice scegliere e su quali colonne. Quest'ultimo si basa su vari fattori come la complessità della query, il tipo di dati, la cardinalità dei dati, il volume delle scritture, la dimensione dei dati, l'architettura del disco, l'infrastruttura (cloud pubblico, cloud privato o on-premise), ecc.
Sebbene l'indicizzazione possa migliorare notevolmente le prestazioni di lettura delle query, può anche rallentare le scritture che colpiscono le colonne indicizzate. Vediamo un esempio:
Impatto degli indici sulle operazioni di LETTURA
Una tabella chiamata emp con circa 1 milione di righe.
LEGGI Performance senza indice
postgres=# select * from emp where eid=10;
eid | ename | peid | did | doj
-----+---------------+--------+------+------------
10 | emp | | 1 | 2018-06-06
(1 row)
Time: 70.020 ms => took about 70+ milli-seconds to respond with on row
LEGGI Performance con un indice
Mettiamo un indice sulla colonna eid e vediamo la differenza
postgres=# create index indx001 on emp ( eid );
CREATE INDEX
postgres=# select * from emp where eid=10;
eid | ename | peid | did | doj
------+-------------+-------+------+------------
10 | emp | | 1 | 2018-06-06
(1 row)
Time: 0.454 ms => 0.4+ milli-seconds!!! thats a huge difference - isn’t it?
Quindi, l'indicizzazione è importante.
Impatto degli indici sulle operazioni di SCRITTURA
Gli indici rallentano le prestazioni delle scritture. Sebbene gli indici abbiano un impatto su tutti i tipi di operazioni di scrittura, esaminiamo alcune analisi sull'impatto degli indici sugli INSERT
Inserimento di 1 milione di righe in una tabella senza indici
postgres=# do $$
postgres$# declare
postgres$# i integer;
postgres$# begin
postgres$# for i in 1..1000000 loop
postgres$# insert into emp values (i,'emp',null,1,current_date);
postgres$# end loop;
postgres$# end $$;
DO
Time: 4818.470 ms (00:04.818) => Takes about 4.8 seconds
Inserimento dello stesso milione di righe con un indice
Creiamo prima un indice
postgres=# create index indx001 on emp ( eid );
CREATE INDEX
postgres=# do $$
postgres$# declare
postgres$# i integer;
postgres$# begin
postgres$# for i in 1..1000000 loop
postgres$# insert into emp values (i,'emp',null,1,current_date);
postgres$# end loop;
postgres$# end $$;
DO
Time: 7825.494 ms (00:07.825) => Takes about 7.8 seconds
Quindi, come possiamo osservare, il tempo INSERT è aumentato dell'80% con un solo indice e può richiedere molto più tempo per terminare quando sono presenti più indici. Può peggiorare ulteriormente quando sono presenti indici basati su funzioni. Questo è ciò con cui devono convivere i DBA! Gli indici aumenteranno le prestazioni di scrittura. Ci sono modi per affrontare questo problema, che dipende dall'architettura del disco. Se il server di database utilizza più file system del disco, gli indici e le tabelle possono essere posizionati su più tablespace che si trovano su più file system del disco. In questo modo è possibile ottenere migliori prestazioni di I/O.
CONSIGLI per la gestione dell'indice
- Capire la necessità degli indici. L'indicizzazione intelligente è fondamentale.
- Evita di creare più indici e sicuramente nessun indici non necessari, questo può davvero peggiorare le prestazioni di scrittura.
- Controlla l'utilizzo degli indici ed elimina gli indici inutilizzati.
- Quando le colonne indicizzate sono soggette a modifiche ai dati, anche gli indici si gonfiano. Quindi, riorganizza regolarmente gli indici.
Partizionamento
Una strategia di partizionamento efficace può ridurre notevolmente i problemi di prestazioni di I/O. Le tabelle di grandi dimensioni possono essere partizionate in base alla logica aziendale. PostgreSQL supporta il partizionamento delle tabelle. Sebbene al momento non supporti completamente tutte le funzionalità, può solo aiutare con alcuni casi d'uso in tempo reale. In PostgreSQL, le tabelle figlio partizionate sono completamente individuali rispetto alla tabella master, il che costituisce un collo di bottiglia. Ad esempio, i vincoli creati nella tabella principale non possono essere ereditati automaticamente nelle tabelle secondarie.
Tuttavia, dal punto di vista del bilanciamento dell'I/O, il partizionamento può davvero aiutare. Tutte le partizioni figlio possono essere suddivise su più tablespace e file system del disco. Le query con un intervallo di date nella clausola "where" che colpiscono la tabella, partizionate in base all'intervallo di date, possono trarre vantaggio dal partizionamento semplicemente scansionando una o due partizioni anziché l'intera tabella.
Punto di controllo
I checkpoint definiscono lo stato coerente del database. Sono fondamentali ed è importante che i checkpoint si verifichino sufficientemente regolarmente per garantire che le modifiche ai dati vengano salvate in modo permanente su disco e che il database sia sempre in uno stato coerente. Detto questo, una configurazione errata dei checkpoint può portare a problemi di prestazioni di I/O. I DBA devono essere meticolosi nella configurazione dei checkpoint per garantire che non vi siano picchi di I/O e anche questo dipende dalla qualità dei dischi e dall'architettura del layout dei file di dati.
Che cosa fa il checkpoint?
In parole povere, i checkpoint garantiranno:
- Tutti i dati salvati vengono scritti nei file di dati sul disco.
- I file clog vengono aggiornati con lo stato del commit.
- I file di registro delle transazioni nella directory pg_xlog (ora pg_wal) vengono riciclati.
Questo spiega come sono i checkpoint ad alta intensità di I/O. Ci sono parametri in postgresql.conf che possono essere configurati/regolati per controllare il comportamento del checkpoint e quei parametri sono max_wal_size, min_wal_size, checkpoint_timeout e checkpoint_completion_target. Questi parametri decideranno con quale frequenza devono verificarsi i checkpoint ed entro quanto tempo devono terminare i checkpoint.
Come capire quale configurazione è migliore per i checkpoint? Come sintonizzarli?
Ecco alcuni suggerimenti:
- Valuta il TPS del database. Valuta il volume totale delle transazioni che si verificano nel database in un giorno lavorativo e identifica anche l'ora in cui il maggior numero di transazioni raggiunge il database.
- Discutere regolarmente con gli sviluppatori di applicazioni e altri team tecnici per comprendere le statistiche sulla velocità di transazione del database e la crescita futura delle transazioni.
- Questo può essere fatto anche dal database:
-
Monitorare il database e valutare il numero di transazioni che si verificano durante la giornata. Questo può essere fatto interrogando le tabelle pgcatalog come pg_stat_user_tables.
-
Valuta il numero di file di archivio wal generati al giorno
-
Monitora per capire come funzionano i checkpoint abilitando il parametro log_checkpoints
2018-06-06 15:03:16.446 IST [2111] LOG: checkpoint starting: xlog 2018-06-06 15:03:22.734 IST [2111] LOG: checkpoint complete: wrote 12112 buffers (73.9%); 0 WAL file(s) added, 0 removed, 25 recycled; write=6.058 s, sync=0.218 s, total=6.287 s; sync files=4, longest=0.178 s, average=0.054 s; distance=409706 kB, estimate=412479 kB
-
Comprendere se la configurazione del checkpoint corrente è sufficientemente buona per il database. Configura il parametro checkpoint_warning (per impostazione predefinita configurato su 30 secondi) per visualizzare gli avvisi seguenti nei file di registro di postgres.
2018-06-06 15:02:42.295 IST [2111] LOG: checkpoints are occurring too frequently (11 seconds apart) 2018-06-06 15:02:42.295 IST [2111] HINT: Consider increasing the configuration parameter "max_wal_size".
-
Cosa significa l'avviso di cui sopra?
I checkpoint si verificano generalmente ogni volta che max_wal_size (1 GB per impostazione predefinita, il che significa 64 file WAL) di file di registro vengono riempiti o quando viene raggiunto checkpoint_timeout (ogni 5 minuti per impostazione predefinita). L'avviso di cui sopra significa che max_wal_size configurato non è adeguato e i checkpoint si verificano ogni 11 secondi, il che a sua volta significa che 64 file WAL nella directory PG_WAL vengono riempiti in soli 11 secondi, il che è troppo frequente. In altre parole, se ci sono transazioni meno frequenti, i checkpoint si verificheranno ogni 5 minuti. Quindi, come suggerisce il suggerimento, aumenta il parametro max_wal_size a un valore più alto, il parametro max_min_size può essere aumentato allo stesso o a un valore inferiore rispetto al precedente.
Un altro parametro critico da considerare dal punto di vista delle prestazioni I/O è checkpoint_completion_target che è configurato per impostazione predefinita su 0.5.
checkpoint_completion_target =0,5 x checkpoint_timeout =2,5 minuti
Ciò significa che i checkpoint hanno 2,5 minuti per sincronizzare i blocchi sporchi sul disco. Sono sufficienti 2,5 minuti? Questo deve essere valutato. Se il numero di blocchi sporchi da scrivere è molto alto, 2,5 minuti possono sembrare molto molto aggressivi ed è allora che si può osservare un picco di I/O. La configurazione del parametro complete_target deve essere eseguita in base ai valori max_wal_size e checkpoint_timeout. Se questi parametri vengono elevati a un valore più alto, valuta la possibilità di aumentare checkpoint_completion_target di conseguenza.
VUOTO, ANALIZZA (con FILLFACTOR)
VACUUM è una delle funzionalità più potenti di PostgreSQL. Può essere utilizzato per rimuovere rigonfiamenti (spazio frammentato) all'interno di tabelle e indici ed è generato dalle transazioni. Il database deve essere sottoposto a VACUUMing regolarmente per garantire una sana manutenzione e prestazioni migliori. Anche in questo caso, non aspirare regolarmente il database può causare seri problemi di prestazioni. ANALYZE deve essere eseguito insieme a VACUUM (VACUUM ANALYZE) per garantire statistiche aggiornate per il pianificatore di query.
L'ANALISI DEL VUOTO può essere eseguita in due modi:manuale, automatico o entrambi. In un ambiente di produzione in tempo reale, generalmente sono entrambe le cose. Il VACUUM automatico è abilitato dal parametro “autovacuum” che di default è configurato su “on”. Con l'autovacuum abilitato, PostgreSQL avvia automaticamente l'aspirazione periodica delle tabelle. I tavoli candidati che necessitano di aspirazione vengono prelevati da processi di autovacuum basati su varie soglie impostate da vari parametri di autovacuum*, questi parametri possono essere modificati / regolati per garantire che i rigonfiamenti dei tavoli vengano eliminati periodicamente. Diamo un'occhiata ad alcuni parametri e al loro utilizzo -
Parametri dell'autovuoto
autovacuum=on | Questo parametro viene utilizzato per abilitare/disabilitare l'autovacuum. L'impostazione predefinita è "on". |
log_autovacuum_min_duration =-1 | Registra la durata del processo di autovuoto. Questo è importante per capire da quanto tempo è stato eseguito il processo di autovacuum. |
autovacuum_max_workers =3 | Numero di processi di autovacuum necessari. Questo dipende da quanto sono aggressive le transazioni del database e da quante CPU puoi offrire per i processi di autovacuum. |
autovacuum_naptime =1 minuto | Tempo di riposo dell'autovuoto tra le esecuzioni dell'autovuoto. |
Parametri che definiscono la soglia per l'avvio del processo di Autovacuum
I lavori di autovacuum iniziano quando viene raggiunta una determinata soglia. Di seguito sono riportati i parametri che possono essere utilizzati per impostare una determinata soglia, in base alla quale inizierà il processo di autovuoto.
autovacuum_vacuum_threshold =50 | La tabella verrà aspirata quando un minimo di 50 righe verrà aggiornato/cancellato in una tabella. |
autovacuum_analyze_threshold =50 | La tabella verrà analizzata quando un minimo di 50 righe verrà aggiornato/cancellato in una tabella. |
autovacuum_vacuum_scale_factor =0,2 | La tabella verrà aspirata quando almeno il 20% delle righe viene aggiornato/cancellato in una tabella. |
autovacuum_analyze_scale_factor =0,1 | La tabella verrà aspirata quando almeno il 10% delle righe viene aggiornato/cancellato in una tabella. |
I parametri sopra soglia possono essere modificati in base al comportamento del database. I DBA dovranno analizzare e identificare le tavole calde e assicurarsi che queste vengano aspirate il più frequentemente possibile per garantire buone prestazioni. Raggiungere un certo valore per questi parametri potrebbe essere una sfida in un ambiente ad alta transazione, in cui le modifiche ai dati avvengono ogni secondo. Molte volte ho notato che i processi di autovacuum richiedono molto tempo per essere completati, finendo per consumare troppe risorse nei sistemi di produzione.
Suggerirei di non dipendere completamente dal processo di autovacuum, il modo migliore è programmare un lavoro notturno di VACUUM ANALYZE in modo da ridurre il carico sull'autovacuum. Per cominciare, considera la possibilità di VACUUM manualmente grandi tavoli con un alto tasso di transazione.
VUOTO PIENO
VACUUM FULL aiuta a recuperare lo spazio gonfio nelle tabelle e negli indici. Questa utilità non può essere utilizzata quando il database è online poiché blocca la tabella. Le tabelle devono essere sottoposte a VACUUM FULL solo quando le applicazioni vengono arrestate. Anche gli indici verranno riorganizzati insieme alle tabelle durante VACUUM FULL.
Diamo un'occhiata all'impatto di VACUUM ANALYZE
Rigonfiamenti:come identificare i rigonfiamenti? Quando vengono generati i rigonfiamenti?
Ecco alcuni test:
Ho una tabella di dimensioni 1 GB con 10 milioni di righe.
postgres=# select pg_relation_size('pgbench_accounts')/1024/1024/1024;
?column?
----------------
1
postgres=# select count(*) From pgbench_accounts ;
count
-----------------
10000000
Diamo un'occhiata all'impatto dei bloat su una semplice query:select * from pgbench_accounts;
Di seguito è riportato il piano esplicativo per la query:
postgres=# explain analyze select * from pgbench_accounts;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------------------
Seq Scan on pgbench_accounts (cost=0.00..263935.00 rows=10000000 width=97)
(actual time=0.033..1054.257 rows=10000000 loops=1)
Planning time: 0.255 ms
Execution time: 1494.448 ms
Ora aggiorniamo tutte le righe nella tabella e vediamo l'impatto della query SELECT sopra.
postgres=# update pgbench_accounts set abalance=1;
UPDATE 10000000
postgres=# select count(*) From pgbench_accounts ;
count
-----------------
10000000
Di seguito è riportato l'EXPLAIN PLAN della query dopo l'esecuzione di UPDATE.
postgres=# explain analyze select * from pgbench_accounts;
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------------------------------
Seq Scan on pgbench_accounts (cost=0.00..527868.39 rows=19999939 width=97)
(actual time=404.474..1520.175 rows=10000000 loops=1)
Planning time: 0.051 ms
Execution time: 1958.532 ms
La dimensione della tabella è aumentata a 2 GB dopo l'AGGIORNAMENTO
postgres=# select pg_relation_size('pgbench_accounts')/1024/1024/1024;
?column?
-----------------
2
Se puoi osservare e confrontare i numeri di costo del precedente EXPLAIN PLAN, c'è un'enorme differenza. Il costo è aumentato di un grande margine. Ancora più importante, se osservi attentamente, il numero di righe (poco più di 19 milioni) scansionate dopo l'AGGIORNAMENTO è più alto, ovvero quasi il doppio delle righe esistenti effettive (10 milioni). Ciò significa che il numero di righe gonfie è di oltre 9 milioni e anche il tempo effettivo è aumentato e il tempo di esecuzione è aumentato da 1,4 secondi a 1,9 secondi.
Quindi, questo è l'impatto del non VACUUMARE la TABELLA dopo l'AGGIORNAMENTO. I numeri EXPLAIN PLAN sopra significano esattamente che il tavolo è gonfio.
Come identificare se il tavolo è gonfio? Usa il modulo contrib pgstattuple:
postgres=# select * from pgstattuple('pgbench_accounts');
table_len | tuple_count | tuple_len | tuple_percent | dead_tuple_count | dead_tuple_len | dead_tuple_percent | free_space | free_percent
------------+-------------+------------+---------------+------------------+----------------+--------------------+------------+--------------
2685902848 | 10000000 | 1210000000 | 45.05 | 9879891 | 1195466811 | 44.51 | 52096468 | 1.94
Il numero sopra indica che metà del tavolo è gonfia.
Analizziamo la tabella sottovuoto e vediamo l'impatto ora:
postgres=# VACUUM ANALYZE pgbench_accounts ;
VACUUM
postgres=# explain analyze select * from pgbench_accounts;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------------------
Seq Scan on pgbench_accounts (cost=0.00..428189.05 rows=10032005 width=97)
(actual time=400.023..1472.118 rows=10000000 loops=1)
Planning time: 4.374 ms
Execution time: 1913.541 ms
Dopo VACUUM ANALYZE, i numeri dei costi sono diminuiti. Ora, il numero di righe scansionate si avvicina a 10 milioni, anche il tempo effettivo e il tempo di esecuzione non sono cambiati molto. Questo perché, sebbene i rigonfiamenti nella tabella siano scomparsi, la dimensione della tabella da scansionare rimane la stessa. Di seguito è riportato l'output di pgstattuple post VACUUM ANALYZE.
postgres=# select * from pgstattuple('pgbench_accounts');
table_len | tuple_count | tuple_len | tuple_percent | dead_tuple_count | dead_tuple_len | dead_tuple_percent | free_space | free_percent
------------+-------------+------------+---------------+------------------+----------------+--------------------+------------+--------------
2685902848 | 10000000 | 1210000000 | 45.05 | 0 | 0 | 0 | 1316722516 | 49.02
Il numero sopra indica che tutti i rigonfiamenti (tuple morte) sono scomparsi.
Diamo un'occhiata all'impatto di VACUUM FULL ANALYZE e vediamo cosa succede:
postgres=# vacuum full analyze pgbench_accounts ;
VACUUM
postgres=# explain analyze select * from pgbench_accounts;
QUERY PLAN
---------------------------------------------------------------------------
Seq Scan on pgbench_accounts (cost=0.00..263935.35 rows=10000035 width=97)
(actual time=0.015..1089.726 rows=10000000 loops=1)
Planning time: 0.148 ms
Execution time: 1532.596 ms
Se osservi, i numeri del tempo effettivo e del tempo di esecuzione sono simili ai numeri prima di UPDATE. Inoltre, la dimensione della tabella è ora ridotta da 2 GB a 1 GB.
postgres=# select pg_relation_size('pgbench_accounts')/1024/1024/1024;
?column?
-----------------
1
Questo è l'impatto di VACUUM FULL.
FILFACTOR
FILLFACTOR è un attributo molto importante che può fare la differenza nella strategia di manutenzione del database a livello di tabella e indice. Questo valore indica la quantità di spazio che deve essere utilizzata dagli INSERT all'interno di un blocco di dati. Il valore predefinito di FILLFACTOR è 100%, il che significa che gli INSERT possono utilizzare tutto lo spazio disponibile in un blocco di dati. Significa anche che non c'è spazio disponibile per gli AGGIORNAMENTI. Questo valore può essere ridotto a un determinato valore per tabelle fortemente aggiornate.
Questo parametro può essere configurato per ogni tabella e un indice. Se FILLFACTOR è configurato sul valore ottimale, puoi vedere la differenza reale nelle prestazioni VACUUM e anche nelle prestazioni delle query. In breve, i valori ottimali di FILLFACTOR assicurano che non venga assegnato un numero non necessario di blocchi.
Diamo un'occhiata allo stesso esempio sopra -
La tabella ha un milione di righe
postgres=# select count(*) From pgbench_accounts ;
count
-----------------
10000000
Prima dell'aggiornamento la dimensione della tabella è di 1 GB
postgres=# select pg_relation_size('pgbench_accounts')/1024/1024/1024;
?column?
--------
1
postgres=# update pgbench_accounts set abalance=1;
UPDATE 10000000
Dopo l'aggiornamento, la dimensione della tabella è aumentata a 2 GB dopo l'AGGIORNAMENTO
postgres=# select pg_relation_size('pgbench_accounts')/1024/1024/1024;
?column?
---------
2
Ciò significa che il numero di blocchi assegnati alla tabella è aumentato del 100%. Se è stato configurato FILLFACTOR, la dimensione della tabella potrebbe non essere aumentata di quel margine.
Come sapere quale valore configurare in FILLFACTOR?
Tutto dipende da quali colonne vengono aggiornate e dalla dimensione delle colonne aggiornate. In generale, sarebbe opportuno valutare il valore FILLFACTOR testandolo nei database UAT. Se le colonne da aggiornare sono ad esempio il 10% dell'intera tabella, considera la possibilità di configurare fillfactor al 90% o all'80%.
Nota importante:
Se modifichi il valore FILLFACTOR per la tabella esistente con i dati, dovrai eseguire un VACUUM FULL o una riorganizzazione della tabella per assicurarti che il valore FILLFACTOR sia attivo per i dati esistenti.
CONSIGLI PER L'ASPIRAZIONE
- Come detto sopra, prendi in considerazione l'esecuzione manuale del lavoro VACUUM ANALYZE ogni notte sui tavoli molto utilizzati anche quando l'autovacuum è abilitato.
- Considera l'esecuzione di VACUUM ANALYZE sulle tabelle dopo l'INSERIMENTO di massa. Questo è importante poiché molti credono che il VACUUM potrebbe non essere necessario dopo gli INSERT.
- Controlla per garantire che le tabelle altamente attive vengano VACUUM regolarmente interrogando la tabella pg_stat_user_tables.
- Usa il modulo contrib pg_stattuple per identificare la dimensione dello spazio gonfio all'interno dei segmenti della tabella.
- Il programma di utilità VACUUM FULL non può essere utilizzato sui sistemi di database di produzione. Prendi in considerazione l'utilizzo di strumenti come pg_reorg o pg_repack che aiuteranno a riorganizzare tabelle e indici online senza blocchi.
- Assicurati che il processo AUTOVACUUM venga eseguito più a lungo durante le ore lavorative (traffico elevato).
- Abilita il parametro log_autovacuum_min_duration per registrare i tempi e la durata dei processi AUTOVACUUM.
- È importante assicurarsi che FILLFACTOR sia configurato su un valore ottimale su tabelle e indici di transazione elevati.
Altri problemi di I/O
Ordinamento del disco
Le query che eseguono l'ordinamento sono un'altra ricorrenza comune nei database di produzione in tempo reale e la maggior parte di queste non può essere evitata. Le query che utilizzano clausole come GROUP BY, ORDER BY, DISTINCT, CREATE INDEX, VACUUM FULL ecc. eseguono l'ordinamento e l'ordinamento può avvenire su disco. L'ordinamento avviene in memoria se la selezione e l'ordinamento vengono effettuati in base a colonne indicizzate. È qui che gli indici compositi giocano un ruolo chiave. Gli indici vengono memorizzati nella cache in modo aggressivo. In caso contrario, se si presenta la necessità di ordinare i dati su disco, le prestazioni rallenteranno drasticamente.
Per garantire che l'ordinamento avvenga in memoria, è possibile utilizzare il parametro work_mem. Questo parametro può essere configurato su un valore tale che l'intero ordinamento possa essere eseguito in memoria. Il vantaggio principale di questo parametro è che, oltre a configurarlo in postgresql.conf, può essere configurato anche a livello di sessione, utente o database. Quanto dovrebbe essere il valore di work_mem? Come sapere quali query eseguono l'ordinamento del disco? Come monitorare le query che eseguono l'ordinamento del disco su un database di produzione in tempo reale?
La risposta è:configurare il parametro log_temp_files su un determinato valore. Il valore è in byte, un valore di 0 registra tutti i file temporanei (insieme alle loro dimensioni) generati sul disco a causa dell'ordinamento del disco. Una volta configurato il parametro, potrai vedere i seguenti messaggi nei file di registro
2018-06-07 22:48:02.358 IST [4219] LOG: temporary file: path "base/pgsql_tmp/pgsql_tmp4219.0", size 200425472
2018-06-07 22:48:02.358 IST [4219] STATEMENT: create index bid_idx on pgbench_accounts(bid);
2018-06-07 22:48:02.366 IST [4219] LOG: duration: 6421.705 ms statement: create index bid_idx on pgbench_accounts(bid);
Il messaggio precedente indica che la query CREATE INDEX stava eseguendo l'ordinamento del disco e ha generato un file di dimensioni 200425472 byte che è 191+ MB. Ciò significa esattamente che il parametro work_mem deve essere configurato su 191+ MB o superiore affinché questa particolare query esegua l'ordinamento della memoria.
Bene, per le query dell'applicazione, il parametro work_mem può essere configurato solo a livello di utente. Prima di farlo, fai attenzione al numero di connessioni che l'utente sta effettuando al database e al numero di query di ordinamento eseguite da quell'utente. Perché PostgreSQL cerca di allocare work_mem a ciascun processo (eseguendo l'ordinamento) in ogni connessione che potrebbe potenzialmente affamare la memoria sul server del database.
Layout del file system del database
La progettazione di un layout del file system del database efficiente e favorevole alle prestazioni è importante dal punto di vista delle prestazioni e della scalabilità. È importante sottolineare che questo non dipende dalle dimensioni del database. In generale, la percezione è che i database di grandi dimensioni necessiteranno di un'architettura del disco ad alte prestazioni, il che NON è vero. Anche se la dimensione del database è di 50 GB, potrebbe essere necessaria una buona architettura del disco. E questo potrebbe non essere possibile senza incorrere in costi aggiuntivi.
Ecco alcuni SUGGERIMENTI per lo stesso:
- Assicurati che il database abbia più tablespace, con tabelle e indici raggruppati in base ai tassi di transazione.
- Il tablespace deve essere posizionato su più file system del disco per un I/O bilanciato. Ciò garantirà inoltre che più CPU entrino in gioco per eseguire transazioni su più dischi.
- Considera la possibilità di posizionare la directory pg_xlog o pg_wal su un disco separato su un database con transazioni elevate.
- Assicurati che *_i parametri di costo siano configurati in base all'infrastruttura
- Utilizzare iostat, mpstat e altri strumenti di monitoraggio I/O per comprendere le statistiche I/O su tutti i dischi e progettare/gestire gli oggetti del database di conseguenza.
PostgreSQL su Cloud
L'infrastruttura è fondamentale per una buona prestazione del database. Le strategie di ingegneria delle prestazioni differiscono in base all'infrastruttura e all'ambiente. È necessario prestare particolare attenzione ai database PostgreSQL ospitati nel cloud. Il benchmarking delle prestazioni per i database ospitati su server fisici barebone in un data center locale può essere completamente diverso dai database ospitati nel cloud pubblico.
In generale, le istanze cloud potrebbero essere un po' più lente e i benchmark differiscono di un margine considerevole, soprattutto in termini di I/O. Esegui sempre i controlli della latenza I/O prima di scegliere/creare un'istanza cloud. Con mia sorpresa, ho appreso che le prestazioni delle istanze cloud possono variare anche a seconda delle regioni, anche se provengono dallo stesso provider cloud. Per spiegare ulteriormente questo, un'istanza cloud con le stesse specifiche costruita in due regioni diverse potrebbe darti risultati di prestazioni diversi.
Carico dati in blocco
Le operazioni di caricamento in blocco dei dati offline sono piuttosto comuni nel mondo dei database. Possono generare un carico di I/O significativo, che a sua volta rallenta le prestazioni del caricamento dei dati. Ho affrontato tali sfide nella mia esperienza come DBA. Spesso, il caricamento dei dati diventa terribilmente lento e deve essere ottimizzato. Ecco alcuni suggerimenti. Intendiamoci, questi si applicano solo alle operazioni di caricamento dei dati offline e non possono essere presi in considerazione per il caricamento dei dati sul database di produzione live.
- Poiché la maggior parte delle operazioni di caricamento dei dati viene eseguita durante l'orario di lavoro, assicurarsi che i seguenti parametri siano configurati durante il caricamento dei dati -
- Configura i valori relativi ai checkpoint sufficientemente grandi in modo che i checkpoint non causino problemi di prestazioni.
- Disattiva full_page_write
- Disattiva l'archiviazione wal
- Configura il parametro synchronous_commit su "off"
- I vincoli di rilascio e gli indici per le tabelle soggette al caricamento dei dati (vincoli e indici possono essere ricreati dopo il caricamento dei dati con un valore work_mem maggiore)
- Se stai eseguendo il caricamento dei dati da un file CSV, Maintenance_work_mem più grande può darti buoni risultati.
- Sebbene ci sarà un significativo vantaggio in termini di prestazioni, NON disattivare il parametro fsync in quanto ciò potrebbe causare il danneggiamento dei dati.
SUGGERIMENTI per l'analisi delle prestazioni del cloud
- Esegui test approfonditi della latenza di I/O utilizzando pgbench. Nella mia esperienza, ho ottenuto risultati di prestazioni piuttosto normali durante i controlli della latenza del disco come parte della valutazione del TPS. Si sono verificati problemi con le prestazioni della cache su alcune istanze di cloud pubblico. Ciò aiuterà a scegliere le specifiche appropriate per l'istanza cloud scelta per i database.
- Le istanze cloud possono funzionare in modo diverso da regione a regione. Un'istanza cloud con determinate specifiche in una regione può fornire risultati di prestazioni diversi rispetto a un'istanza cloud con le stesse specifiche in un'altra regione. I miei test pgbench eseguiti su più istanze cloud (tutte le stesse specifiche con lo stesso fornitore di cloud) in diverse regioni mi hanno dato risultati diversi su alcune di esse. Questo è importante soprattutto durante la migrazione al cloud.
- Le prestazioni delle query sul cloud potrebbero richiedere un approccio di ottimizzazione diverso. I DBA dovranno utilizzare i parametri *_cost per garantire che vengano generati piani di esecuzione delle query sani.
Strumenti per monitorare le prestazioni di PostgreSQL
There are various tools to monitor PostgreSQL performance. Let me highlight some of those.
- pg_top is a GREAT tool to monitor PostgreSQL database dynamically. I would highly recommend this tool for DBAs for various reasons. This tool has numerous advantages, let me list them out:
- pg_top tool uses textual interface and is similar to Unix “top” utility.
- Will clearly list out the processes and the hardware resources utilized. What excites me with this tool is that it will clearly tell you if a particular process is currently on DISK or CPU - in my view that’s excellent. DBAs can clearly pick the process running for longer time on the disk.
- You can check the EXPLAIN PLAN of the top SQLs dynamically or instantly
- You can also find out what Tables or Indexes are being scanned instantly
- Nagios is a popular monitoring tool for PostgreSQL which has both open-source and commercial versions. Open source version should suffice for monitoring. Custom Perl scripts can be built and plugged into Nagios module.
- Pgbadger is a popular tool which can be used to analyze PostgreSQL log files and generate performance reports. This report can be used to analyze the performance of checkpoints, disk sorting.
- Zabbix is another popular tool used for PostgreSQL monitoring.
ClusterControl is an up-and-coming management platform for PostgreSQL. Apart from monitoring, it also has functionality to deploy replication setups with load balancers, automatic failover, backup management, among others.