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

Configurazione di PostgreSQL per l'osservabilità

PostgreSQL viene fornito con una pletora di opzioni di configurazione, ma la modifica delle impostazioni predefinite di alcune di queste opzioni migliora drasticamente l'osservabilità del server PostgreSQL. Ti consigliamo di impostare e configurare queste opzioni prima che i problemi appaiano in produzione, poiché possono fornire informazioni essenziali per comprendere e risolvere tali problemi.

Continua a leggere per saperne di più sulle impostazioni e sulle estensioni che espongono metriche e informazioni sul funzionamento interno del tuo server PostgreSQL.

Prefisso riga registro

Il prefisso_linea_registro l'opzione di configurazione determina cosa scrive PostgreSQL all'inizio di ogni riga di registro. L'impostazione predefinita dipende dalla distribuzione Linux specifica o dalla soluzione gestita che stai utilizzando, ma il più delle volte non include alcuni elementi che possono rivelarsi molto utili per rintracciare i client che si comportano male. Prova questo log_line_prefix :

log_line_prefix = '%m [%p] %a %u %d %h '

Include il timestamp (%m ), il PID del processo di back-end (%p ), il nome dell'applicazione (%a ) del client, il nome utente con cui il client si è connesso (%u ), il database a cui il client si è connesso (%d ) e il nome host o IP da cui proviene la connessione (%h ). Ciò si traduce in logline come questa:

2021-01-30 05:06:03.675 UTC [73] psql postgres bench 172.17.0.1 ERROR:  relation "pgbench_akkounts" does not exist at character 15
2021-01-30 05:06:03.675 UTC [73] psql postgres bench 172.17.0.1 STATEMENT:  select * from pgbench_akkounts;

che sono molto più utili di quelli predefiniti. Puoi vedere che un client si è connesso da 172.17.0.1 come utente postgres nel database panca e l'applicazione era psql . Sicuramente un miglioramento rispetto all'opzione predefinita, che mostra solo questo:

2021-01-30 05:13:22.630 UTC [63] ERROR:  relation "pgbench_akkounts" does not exist at character 15
2021-01-30 05:13:22.630 UTC [63] STATEMENT:  select * from pgbench_akkounts;

Registrazione delle query lente

PostgreSQL può essere configurato per registrare le query che richiedono più di un determinato periodo di tempo per essere eseguite. Questi vanno nello stesso file di registro; non esiste un file di registro delle query lento separato come in MySQL.

Per registrare le istruzioni che richiedono più di 1 secondo per essere eseguite, usa il log_min_duration_statement opzione come questa:

log_min_duration_statement = 1s

Nota che log_min_duration_statement prenderà in considerazione tutte le istruzioni (incluse, ad esempio, le dichiarazioni di amministrazione di lunga durata come REINDEX TABLE ) e non solo query (SELECT ). Ecco alcune voci di registro prodotte da questa opzione:

2021-01-30 08:42:57.473 UTC [62] psql postgres postgres 172.17.0.1 LOG:  duration: 1016.283 ms  statement: select pg_sleep(1);
2021-01-30 08:52:00.541 UTC [62] psql postgres postgres 172.17.0.1 LOG:  duration: 1118.277 ms  statement: select pg_sleep(1.1);

Se ciò si traduce in troppi registri di affermazioni simili, puoi dire a Postgres di registrarne solo una percentuale, utilizzando:

log_min_duration_statement = -1
log_min_duration_sample = 1s
log_statement_sample_rate = 0.25

Questo registra solo il 25% delle istruzioni che diventano idonee per la registrazione (quelle che hanno richiesto più di 1 secondo per essere eseguite). L'output del registro è lo stesso di prima. Non c'è modo di sapere quante dichiarazioni idonee non sono state registrate.

Per registrare tutte le istruzioni, insieme al tempo impiegato per eseguirle, usa log_statement opzione invece:

log_statement = mod
log_duration = on

L'opzione "mod" dice a Postgres di registrare DDL e istruzioni di modifica dei dati. Ciò risulta in registri come questi:

2021-01-30 08:35:08.985 UTC [64] pgbench postgres bench 172.17.0.1 LOG:  statement: insert into pgbench_tellers(tid,bid,tbalance) values (10,1,0)
2021-01-30 08:35:08.985 UTC [64] pgbench postgres bench 172.17.0.1 LOG:  duration: 0.241 ms

Tieni presente che non possibile campionare la registrazione delle istruzioni abilitata in questo modo, tutte le dichiarazioni verranno registrate e ti ritroverai con tonnellate di voci di registro.

Blocchi e deadlock di registrazione

Le query possono attendere troppo a lungo per acquisire un blocco. In genere, viene impostato un limite massimo di attesa utilizzando l'opzione lock_timeout , di solito sul lato client. Se una query ha atteso così a lungo per acquisire un blocco, Postgres annullerà l'esecuzione di questa query e registrerà un errore:

2021-01-30 09:35:52.415 UTC [67] psql postgres testdb 172.17.0.1 ERROR:  canceling statement due to lock timeout
2021-01-30 09:35:52.415 UTC [67] psql postgres testdb 172.17.0.1 STATEMENT:  cluster t;

Supponiamo che tu voglia impostare un timeout di blocco di 1 minuto, ma registra le query che attendono i blocchi per più di, diciamo, 30 secondi. Puoi farlo usando:

log_lock_waits = on
deadlock_timeout = 30s

Questo creerà log come questo:

2021-01-30 09:49:22.331 UTC [70] psql postgres testdb 172.17.0.1 LOG:  process 70 still waiting for ShareLock on transaction 493 after 30009.004 ms
2021-01-30 09:49:22.331 UTC [70] psql postgres testdb 172.17.0.1 DETAIL:  Process holding the lock: 68. Wait queue: 70.
2021-01-30 09:49:22.331 UTC [70] psql postgres testdb 172.17.0.1 CONTEXT:  while locking tuple (0,3) in relation "t"
2021-01-30 09:49:22.331 UTC [70] psql postgres testdb 172.17.0.1 STATEMENT:  select * from t for update;

L'uso di deadlock_timeout non è un errore di battitura:è il valore utilizzato dal meccanismo di lock waitlogging. Idealmente, avrebbe dovuto esserci qualcosa come log_min_duration_lock_wait ,ma sfortunatamente non è così.

In caso di deadlock effettivi, Postgres interromperà le transazioni bloccate dopo deadlock_timeout durata e registrerà le dichiarazioni incriminate. Non è necessaria alcuna configurazione esplicita.

2021-01-30 09:55:37.724 UTC [68] psql postgres testdb 172.17.0.1 LOG:  process 68 detected deadlock while waiting for ShareLock on transaction 496 after 30007.633 ms
2021-01-30 09:55:37.724 UTC [68] psql postgres testdb 172.17.0.1 DETAIL:  Process holding the lock: 70. Wait queue: .
2021-01-30 09:55:37.724 UTC [68] psql postgres testdb 172.17.0.1 CONTEXT:  while locking tuple (0,3) in relation "t"
2021-01-30 09:55:37.724 UTC [68] psql postgres testdb 172.17.0.1 STATEMENT:  select * from t where a=4 for update;
2021-01-30 09:55:37.725 UTC [68] psql postgres testdb 172.17.0.1 ERROR:  deadlock detected
2021-01-30 09:55:37.725 UTC [68] psql postgres testdb 172.17.0.1 DETAIL:  Process 68 waits for ShareLock on transaction 496; blocked by process 70.
        Process 70 waits for ShareLock on transaction 495; blocked by process 68.
        Process 68: select * from t where a=4 for update;
        Process 70: select * from t where a=0 for update;
2021-01-30 09:55:37.725 UTC [68] psql postgres testdb 172.17.0.1 HINT:  See server log for query details.
2021-01-30 09:55:37.725 UTC [68] psql postgres testdb 172.17.0.1 CONTEXT:  while locking tuple (0,3) in relation "t"
2021-01-30 09:55:37.725 UTC [68] psql postgres testdb 172.17.0.1 STATEMENT:  select * from t where a=4 for update;

Registrazione degli autovacuum

Il processo di autovacuum si avvia quando Postgres determina che i dati in una tabella sono cambiati sufficientemente per giustificare un vuoto e un'analisi. Per tenere d'occhio questo processo, abilita la registrazione delle esecuzioni di autovacuum:

log_autovacuum_min_duration = 250ms

Ecco una voce di esempio causata da modifiche eccessive a una tabella:

2021-01-30 10:23:33.201 UTC [63]     LOG:  automatic vacuum of table "postgres.public.t": index scans: 0
        pages: 0 removed, 95 remain, 0 skipped due to pins, 0 skipped frozen
        tuples: 8991 removed, 10000 remain, 0 are dead but not yet removable, oldest xmin: 492
        buffer usage: 215 hits, 4 misses, 4 dirtied
        avg read rate: 1.885 MB/s, avg write rate: 1.885 MB/s
        system usage: CPU: user: 0.01 s, system: 0.00 s, elapsed: 0.01 s
        WAL usage: 244 records, 1 full page images, 67984 bytes
2021-01-30 10:23:33.222 UTC [63]     LOG:  automatic analyze of table "postgres.public.t" system usage: CPU: user: 0.01 s, system: 0.00 s, elapsed: 0.01 s

Nota che l'autovuoto in genere attiverà un'analisi dopo il vuoto e anche questo verrà registrato.

Questi registri ti aiuteranno a capire come regolare al meglio i parametri dell'autovacuum e ti aiuteranno a indagare se e quando l'autovacuum non è efficace come pensavi.

Checkpoint di registrazione

Il checkpoint è il processo di push delle modifiche registrate da WAL nei file effettivi che supportano le tabelle. Idealmente, i checkpoint dovrebbero verificarsi a intervalli regolari e non troppo frequenti, poiché si tratta di un processo ad alta intensità di CPU e disco. Per vari motivi, i checkpoint sono anche forzati a verificarsi prima del prossimo orario programmato e ciò si traduce in una riduzione delle prestazioni delle query.

Per tenere d'occhio la frequenza e l'efficienza dei checkpoint, abilita la registrazione dei checkpoint:

log_checkpoints = on

Questo dice a PostgreSQL di registrare quanto segue ogni volta che si verifica un checkpoint:

2021-01-30 10:05:57.085 UTC [56]     LOG:  checkpoint starting: immediate force wait
2021-01-30 10:05:57.159 UTC [56]     LOG:  checkpoint complete: wrote 0 buffers (0.0%); 0 WAL file(s) added, 0 removed, 0 recycled; write=0.000 s, sync=0.000 s, total=0.074 s; sync files=0, longest=0.000 s, average=0.000 s; distance=0 kB, estimate=0 kB

La prima riga contiene i flag che il backend ha passato al checkpointer. Puoi vedere che la "forza" ha causato un checkpoint anche se non c'erano modifiche in sospeso al checkpoint. Se "immediato" non fosse stato specificato, il checkpoint avrebbe effettuato il checkpoint fino a checkpoint_completion_target .

Altre impostazioni lato server

Ci sono un paio di altre impostazioni che puoi attivare nella tua configurazione di PostgreSQL che ti aiuteranno a diagnosticare i problemi:

  • track_io_timing - impostandolo su attivo ti consente di vedere il tempo trascorso in I/O su disco per ciascuna query (combinato con l'estensione pg_stat_statements descritta di seguito). Vedi i documenti su un avvertimento per attivarlo, ma dovrebbe essere sicuro su quasi tutti i moderni Linux. È impossibile visualizzare il costo di I/O del disco di una query senza attivarlo.
  • track_commit_timestamp - impostandolo su attivo può essere utile nel debug dei ritardi di replica e in altri problemi relativi alla replica.

Query statistiche tramite pg_stat_statements

L'estensione pg_stat_statements è un accessorio essenziale per qualsiasi distribuzione di PostgreSQL. Raccoglie e registra le statistiche per ogni query eseguita e le presenta come una vista chiamata "pg_stat_statements". Questa è un'estensione, il che significa che devi installarla esplicitamente in ogni database per cui desideri i dati, usando il comando:

CREATE EXTENSION pg_stat_statements;

Poiché l'estensione si basa su un .so , dovrai caricarlo utilizzando shared_preload_libraries :

shared_preload_libraries = 'pg_stat_statements'

Questo purtroppo richiede un riavvio del server PostgreSQL; quindi assicurati di farlo prima di andare in diretta.

Se hai eseguito l'aggiornamento da una versione precedente di PostgreSQL, assicurati di aggiornare anche la tua estensione pg_stat_statement, utilizzando:

ALTER EXTENSION pg_stat_statements UPDATE;

L'estensione pg_stat_statements non registra nulla, viene utilizzata tramite interrogazione della vista omonima. Per maggiori dettagli, consulta la documentazione ufficiale.

Piani di esecuzione query tramite auto_explain

auto_explain è un'altra estensione presente nel core PostgreSQL. Può registrare i piani di esecuzione di query lente. Deve solo essere aggiunto ashared_preload_libraries , e non deve essere installato come estensione. Ha anche un paio di altre opzioni che in genere devono essere impostate su valori non predefiniti:

shared_preload_libraries = 'pg_stat_statements,auto_explain'

auto_explain.log_min_duration = 1s
auto_explain.log_analyze = on
auto_explain.log_buffers = on
auto_explain.log_triggers = on
auto_explain.log_timing = on
auto_explain.log_verbose = on
auto_explain.log_format = json

Quanto sopra registra il piano di esecuzione per qualsiasi query che richiede più di 1 secondo per essere completata. Ecco un esempio di output:

2021-01-30 11:28:25.977 UTC [64] psql postgres postgres 172.17.0.1 LOG:  duration: 1.305 ms  plan:
        {
          "Query Text": "SELECT n.nspname as \"Schema\",\n  c.relname as \"Name\",\n  CASE c.relkind WHEN 'r' THEN 'table' WHEN 'v' THEN 'view' WHEN 'm' THEN 'materialized view' WHEN 'i' THEN 'index' WHEN 'S' TH
EN 'sequence' WHEN 's' THEN 'special' WHEN 'f' THEN 'foreign table' WHEN 'p' THEN 'table' WHEN 'I' THEN 'index' END as \"Type\",\n  pg_catalog.pg_get_userbyid(c.relowner) as \"Owner\"\nFROM pg_catalog.pg_class c
\n     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace\nWHERE c.relkind IN ('r','p','v','m','S','f','')\n      AND n.nspname <> 'pg_catalog'\n      AND n.nspname <> 'information_schema'\n      AND
n.nspname !~ '^pg_toast'\n  AND pg_catalog.pg_table_is_visible(c.oid)\nORDER BY 1,2;",
          "Plan": {
            "Node Type": "Sort",
            "Parallel Aware": false,
            "Startup Cost": 32.93,
            "Total Cost": 33.01,
            "Plan Rows": 32,
            "Plan Width": 224,
            "Actual Startup Time": 1.292,
            "Actual Total Time": 1.298,
            "Actual Rows": 0,
[... lots of text snipped ...]

Per ulteriori informazioni su auto_explain, consulta i documenti ufficiali.

Le estensioni pg_stat_statements e auto_explain sono le uniche due opzioni ampiamente supportate di PostgreSQL per la gestione delle prestazioni delle query e la gestione del piano delle query. È utile conoscere queste due funzionalità e pianificare in anticipo l'utilizzo in produzione.

Nome dell'applicazione

Il nome dell'applicazione è un parametro lato client e di solito può essere impostato in DSN o in stringhe di connessione in stile libpq che l'applicazione utilizza per le informazioni di connessione. Molti strumenti e utilità nell'ecosistema PostgreSQL comprendono il nome dell'applicazione e aiuta a impostarlo su un valore significativo, ad esempio:

application_name = weekly-revenue-report

Questo verrebbe impostato per ogni applicazione client che si connette al tuo server PostgreSQL.