Configurazione della Replica Sincrona per l'Alta Disponibilità in PostgreSQL
Impara a configurare l'alta disponibilità di PostgreSQL con perdita di dati zero (RPO=0) utilizzando la replica sincrona in streaming. Questo tutorial passo-passo copre le configurazioni essenziali per `wal_level`, gli slot di replica, `pg_basebackup` e l'impostazione corretta dei parametri `synchronous_commit` sui server primario e standby per garantire la durabilità delle transazioni in ambienti critici.
Configurazione della Replica Sincrona per l'Alta Disponibilità in PostgreSQL
Configurare PostgreSQL per l'alta disponibilità (HA) di solito inizia con una domanda difficile: quanti dati puoi permetterti di perdere se il server primario scompare subito dopo un commit? Con la normale replica asincrona in streaming, la risposta è "forse alcuni". Il primario può dire all'applicazione che una transazione è stata committata prima che lo standby abbia ricevuto o riprodotto il record WAL. Se il primario fallisce durante quella piccola finestra, lo standby promosso potrebbe non contenere le ultime transazioni committate.
La replica sincrona in streaming cambia questo compromesso. PostgreSQL attende uno o più standby nominati prima di segnalare il successo del commit. A seconda del livello di synchronous_commit, lo standby potrebbe dover solo scrivere il WAL nel sistema operativo, flusharlo su storage durevole o riprodurlo in modo che le query sullo standby possano vederlo. Questo può darti un RPO di zero per le transazioni committate, ma significa anche che il percorso di scrittura ora dipende dalla rete e dalla salute dello standby.
Questo compromesso è importante. La replica sincrona è adatta per quel piccolo insieme di dati in cui perdere anche una singola transazione riconosciuta è inaccettabile: pagamenti, saldi conti, prenotazioni di inventario, stato degli ordini, tracce di audit. Spesso è poco adatta per log di eventi ad alto volume, dati di clickstream, metriche o carichi di lavoro in cui la disponibilità e la latenza sono più importanti della perfetta durabilità tra i nodi. Prima di abilitarla globalmente, decidi quale parte del tuo carico di lavoro ne ha effettivamente bisogno.
Prerequisiti
Prima di iniziare, assicurati di avere due server PostgreSQL configurati (Primario e Standby) che eseguono versioni major identiche di PostgreSQL. Entrambi i server devono avere connettività di rete. Per questa guida, assumiamo:
- Hostname/IP Primario:
pg_primary - Hostname/IP Standby:
pg_standby - Utente di Replica:
repl_user - Nome Database:
mydb
Hai anche bisogno di un backup funzionante e di una finestra di manutenzione per il backup di base iniziale. Gli esempi assumono PostgreSQL 12 o successivo, dove la modalità standby è controllata con standby.signal e le impostazioni di connessione sono solitamente scritte da pg_basebackup -R.
Passo 1: Configurazione del Server Primario
Il server primario richiede impostazioni specifiche per abilitare la replica in streaming e gestire il Write-Ahead Log (WAL) richiesto dai commit sincroni.
A. Modifica di postgresql.conf sul Primario
Modifica il file postgresql.conf del server primario. I seguenti parametri sono obbligatori per la replica in streaming:
# --- Richiesto per la Replica ---
listen_addresses = '*' # Permette connessioni dallo standby
wal_level = replica # Deve essere 'replica' o superiore (es. 'logical')
max_wal_senders = 10 # Massimo connessioni concorrenti dagli standby
max_replication_slots = 10 # Slot necessari per flussi di replica persistenti
# --- Essenziale per il Commit Sincrono ---
synchronous_standby_names = 'FIRST 1 (standby1)' # Specifica gli standby richiesti tramite application_name
# --- Opzionale ma Raccomandato ---
wal_log_hints = on # Raccomandato per una replica più sicura, anche se aumenta il volume WAL
shared_preload_libraries = 'pg_stat_statements' # Se si utilizza il monitoraggio
Spiegazione dei Parametri Chiave:
wal_level = replica: Questo assicura che nel WAL vengano scritte informazioni sufficienti per permettere a un server standby di ricostruire lo stato del database. Per i commit sincroni, questo livello è il requisito minimo.synchronous_standby_names: Questa è l'impostazione principale per definire quali standby devono riconoscere le scritture. I nomi qui sono valori diapplication_namedella connessione di replica, non nomi di slot di replica.FIRST 1 (standby1)significa che PostgreSQL attende il primo standby sincrono disponibile da quella lista.ANY 1 (standby1, standby2)significa che qualsiasi standby elencato può soddisfare il commit.
B. Configurazione dell'Autenticazione Basata su Host (pg_hba.conf)
Il server primario deve permettere all'utente di replica dal/i server standby di connettersi per scopi di replica.
Aggiungi una voce a pg_hba.conf sul primario:
# TYPE DATABASE USER ADDRESS METHOD
host replication repl_user pg_standby/32 scram-sha-256
Sostituisci pg_standby/32 con l'indirizzo IP effettivo o la subnet del tuo server standby.
C. Creazione dello Slot di Replica e dell'Utente
Connettiti a PostgreSQL sul server primario per creare l'utente necessario e lo slot di replica.
1. Crea Utente di Replica:
CREATE ROLE repl_user WITH REPLICATION LOGIN PASSWORD 'a_strong_password';
2. Crea Slot di Replica:
Questo slot assicura che i segmenti WAL vengano conservati fino a quando lo standby non conferma la ricezione, impedendo allo standby di rimanere così indietro da richiedere un nuovo backup di base. Gli slot sono utili, ma possono anche riempire i dischi se uno standby è spento per molto tempo, quindi monitora il WAL conservato.
SELECT pg_create_physical_replication_slot('standby1_slot');
Il nome dello slot non deve corrispondere a synchronous_standby_names. In questo esempio, standby1 è il application_name usato per la selezione dello standby sincrono, mentre standby1_slot è lo slot di replica fisico usato per la conservazione del WAL.
D. Riavvio del Primario
Applica tutte le modifiche di configurazione riavviando il servizio PostgreSQL sul server primario.
sudo systemctl restart postgresql
Passo 2: Configurazione del Server Standby
Il server standby è configurato per ricevere in streaming i record WAL dal primario utilizzando una configurazione di recovery.
A. Backup di Base
Prima di avviare lo streaming, lo standby necessita di una copia completa della directory dei dati del primario. Ferma prima PostgreSQL sullo standby.
sudo systemctl stop postgresql
Esegui il backup di base utilizzando pg_basebackup. Sostituisci i percorsi e i dettagli di connessione secondo necessità:
# Esempio utilizzando l'utility pg_basebackup
pg_basebackup -h pg_primary -D /var/lib/postgresql/15/main/ -U repl_user -P -Xs -R -W
-D: La directory dati di destinazione sullo standby.-U: L'utente di replica.-P: Mostra il progresso.-Xs: Includi i file WAL necessari durante il backup di base.-R: Crea automaticamente il filestandby.signale genera le impostazioni di connessione necessarie inpostgresql.auto.conf(o configurazione di recovery).
B. Configurazione di postgresql.conf sullo Standby
Sullo standby, assicurati che PostgreSQL sappia come riconnettersi al primario. Il dettaglio chiave per la replica sincrona è application_name; deve corrispondere al nome elencato in synchronous_standby_names.
# --- Richiesto sullo Standby ---
primary_conninfo = 'host=pg_primary port=5432 user=repl_user password=a_strong_password application_name=standby1'
primary_slot_name = 'standby1_slot'
hot_standby = on # Permette query di lettura durante la modalità recovery/standby
C. Avvio dello Standby
Avvia il servizio PostgreSQL sul server standby.
sudo systemctl start postgresql
Passo 3: Verifica e Test del Commit Sincrono
Una volta che entrambi i server sono in esecuzione, verifica la connessione e poi testa il comportamento sincrono.
A. Verifica dello Stato della Replica
Connettiti al database primario e controlla la vista pg_stat_replication:
SELECT client_addr, application_name, state, sync_state FROM pg_stat_replication;
Dovresti vedere una voce per standby1 con sync_state uguale a sync. Se mostra potential, lo standby è connesso ma non è attualmente quello che soddisfa i commit sincroni. Se mostra async, PostgreSQL non lo sta trattando come uno standby sincrono; controlla l'ortografia di application_name e synchronous_standby_names.
B. Test del Commit Sincrono
Il parametro globale che determina quanto PostgreSQL attende è synchronous_commit. Per RPO=0, devi usare un valore che forza la sincronizzazione.
1. Impostazione del Comportamento Globale
Se hai configurato synchronous_standby_names sul primario come mostrato nel Passo 1, il valore predefinito synchronous_commit = on attende fino a quando lo standby sincrono non ha flushato il record WAL su storage durevole. remote_write attende fino a quando lo standby ha scritto il record WAL nel sistema operativo, che di solito è più veloce ma non altrettanto forte se l'host standby si blocca prima del flush. remote_apply attende fino a quando lo standby ha riprodotto la transazione, il che è utile quando la tua applicazione legge dallo standby immediatamente dopo aver scritto sul primario.
Per la maggior parte delle configurazioni HA con perdita di dati zero, on è il punto di partenza pratico. Usa remote_apply solo quando il comportamento di lettura-dopo-scrittura sullo standby è abbastanza importante da giustificare la latenza aggiuntiva.
# In postgresql.conf sul Primario
synchronous_commit = on
Avvertenza: Il commit sincrono può aumentare notevolmente la latenza di scrittura rispetto alle modalità asincrone (
offolocal). La latenza aggiuntiva deriva dai round trip di rete, dalla velocità di scrittura WAL dello standby e, perremote_apply, dalla velocità di riproduzione.
2. Test all'Interno di una Transazione
Per testare a livello transazionale (senza richiedere una modifica globale della configurazione), puoi impostarlo per sessione o transazione:
-- Connettiti al Primario
BEGIN;
SET LOCAL synchronous_commit = on;
INSERT INTO sales (item, amount) VALUES ('Widget A', 100);
-- Questa INSERT prepara il WAL che deve essere riconosciuto dallo standby sincrono.
COMMIT;
-- Il COMMIT ha successo solo dopo che lo standby riconosce la scrittura WAL.
Se nessuno standby sincrono configurato è disponibile al momento del commit, il commit attende. Questo è lo scopo della funzionalità, ma può sorprendere i team durante un'interruzione: il primario potrebbe essere ancora attivo, eppure le scritture sembrano congelate perché PostgreSQL sta aspettando un riconoscimento sincrono. Non esiste un'impostazione generale di PostgreSQL che "ricada" automaticamente sul commit asincrono quando uno standby sincrono scompare. Se desideri quel comportamento, i tuoi strumenti HA devono modificare synchronous_standby_names, oppure devi avere un runbook per farlo manualmente dopo aver deciso che la disponibilità è più importante della perdita di dati zero.
Un Pattern Più Sicuro a Due Standby
Un singolo standby sincrono offre una forte durabilità mentre tutto è sano, ma crea anche un singolo punto di disponibilità di scrittura. Se quello standby è giù, lento o isolato dal primario, i commit attendono. In produzione, un pattern comune è eseguire almeno due standby e richiedere un riconoscimento sincrono:
synchronous_standby_names = 'ANY 1 (standby1, standby2)'
Con questa configurazione, entrambi gli standby possono soddisfare il commit. Se standby1 si sta riavviando, standby2 può ancora riconoscere le scritture. Devi comunque monitorare entrambe le repliche, perché un'interruzione prolungata su uno standby può far sì che il suo slot di replica conservi una grande quantità di WAL, ma è meno probabile che il primario si blocchi a causa del guasto di un singolo standby.
Richiedere due riconoscimenti è possibile:
synchronous_standby_names = 'ANY 2 (standby1, standby2, standby3)'
Questa è una scelta di durabilità più rigorosa. Di solito è riservata ad ambienti con collegamenti a latenza molto bassa e una chiara ragione per richiedere più di una copia remota prima del commit. Per molti database applicativi, "uno qualsiasi di due standby vicini" è il miglior equilibrio.
Cosa Monitorare Dopo Averlo Abilitato
Non fermarti a "lo standby si connette." La replica sincrona può funzionare tecnicamente mentre la latenza percepita dall'utente peggiora. Osserva questi segnali dopo il rollout:
SELECT
application_name,
client_addr,
state,
sync_state,
write_lag,
flush_lag,
replay_lag
FROM pg_stat_replication;
Sul primario, sync_state = 'sync' ti dice quale standby è attualmente sincrono. write_lag, flush_lag e replay_lag aiutano a spiegare dove va il tempo. Se write_lag è alto, sospetta problemi di rete o pressione di scrittura WAL sullo standby. Se flush_lag è alto, sospetta lo storage. Se solo replay_lag è alto, lo standby potrebbe ricevere WAL ma applicarlo lentamente a causa di I/O, CPU, lock o query a lunga esecuzione sullo standby.
Monitora anche la conservazione degli slot:
SELECT
slot_name,
active,
pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS retained_wal
FROM pg_replication_slots;
Uno slot di replica protegge uno standby, ma non protegge il tuo disco. Se uno slot è inattivo e il WAL conservato continua a crescere, o sistemi rapidamente lo standby o elimina lo slot dopo aver confermato che non ti serve più.
Un Piano di Rollout Pratico
Per un sistema di produzione occupato, tratta la replica sincrona come un cambiamento graduale piuttosto che una modifica di configurazione di una riga.
Prima, costruisci lo standby in modo asincrono e lascialo funzionare per un po'. Conferma che riesca a tenere il passo durante i periodi di picco di scrittura. Se rimane indietro in modo asincrono, danneggerà la latenza di commit quando diventerà sincrono.
Secondo, imposta application_name e verifica che il primario veda lo standby esattamente come ti aspetti in pg_stat_replication. Gli errori di ortografia sono comuni qui perché synchronous_standby_names corrisponde al application_name runtime, non all'hostname e non allo slot.
Terzo, abilita la replica sincrona durante una finestra a basso traffico e osserva la latenza di commit dal lato dell'applicazione. Le metriche di PostgreSQL possono sembrare a posto mentre il pool di connessioni dell'applicazione si accumula perché le transazioni ora mantengono le connessioni un po' più a lungo.
Infine, scrivi la decisione di fallimento. Se lo standby sincrono è sparito e il primario sta aspettando i commit, chi è autorizzato a rilassare synchronous_standby_names? A quali condizioni? Come verificherai se il vecchio primario o il vecchio standby contiene i dati più recenti prima di riunire i nodi? Queste sono decisioni operative, non solo impostazioni di database.
Best Practice per HA Sincrona
- Usa Standby Dedicati: Assegna solo standby fisicamente vicini (bassa latenza) al primario alla tua lista di replica sincrona. L'alta latenza si manifesterà direttamente nel tempo di commit.
- Monitora il Ritardo di Replica: Anche in modalità sincrona, monitora il ritardo dello standby. Uno standby lento che è ancora tecnicamente 'sync' ma impiega troppo tempo per elaborare il WAL può comunque influenzare l'esperienza utente.
- Pianifica il Compromesso di Disponibilità: Decidi in anticipo se un operatore può rimuovere temporaneamente uno standby mancante da
synchronous_standby_namesdurante un incidente. - Usa Più Standby: Per una migliore disponibilità di scrittura, configura
synchronous_standby_names = 'ANY 1 (standby1, standby2)'in modo che entrambi gli standby possano riconoscere i commit.