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

Utilizzo della replica logica PostgreSQL per mantenere un server TEST di lettura/scrittura sempre aggiornato

In questo post del blog parleremo della replica logica in PostgreSQL:i suoi casi d'uso, informazioni generali sullo stato di questa tecnologia e un caso d'uso speciale in particolare su come configurare un nodo di abbonato (replica) del server primario in ordine funzionare come server di database per l'ambiente di test e le sfide incontrate.

Introduzione

La replica logica, introdotta ufficialmente in PostgreSQL 10, è l'ultima tecnologia di replica offerta dalla comunità di PostgreSQL. La replica logica è una continuazione dell'eredità della replica fisica con la quale condivide molte idee e codice. La replica logica funziona come la replica fisica che utilizza il WAL per registrare le modifiche logiche indipendentemente dalla versione o dall'architettura specifica. Per poter fornire una replica logica all'offerta principale, la comunità di PostgreSQL ha fatto molta strada.

Tipi di replica e cronologia della replica PostgreSQL

I tipi di replica nei database possono essere classificati come segue:

  • Replica fisica (AKA binaria)
    • Livello del sistema operativo (replica vSphere)
    • Livello file system (DRBD)
    • Livello database (basato su WAL)
  • Replica logica (livello database)
    • Basato su trigger (DBMirror, Slony)
    • Middleware (pgpool)
    • Basato su WAL (pglogical, replica logica)

La tabella di marcia che porta alla replica logica basata su WAL di oggi era:

  • 2001:DBMirror (basato su trigger)
  • 2004:Slony1 (basato su trigger), pgpool (middleware)
  • 2005:PITR (basato su WAL) introdotto in PostgreSQL 8.0
  • 2006:Warm standby in PostgreSQL 8.2
  • 2010:replica fisica dello streaming, hot standby in PostgreSQL 9.0
  • 2011:replica in streaming sincrona in PostgreSQL 9.1
  • 2012:replica in streaming a cascata in PostgreSQL 9.2
  • 2013:Lavoratori in background in PostgreSQL 9.3
  • 2014:API di decodifica logica, slot di replica. (Le basi per la replica logica) in PostgreSQL 9.4
  • 2015:2ndQuadrant introduce pglogical, l'antenato o Replicazione logica
  • 2017:Replica logica nel core PostgreSQL 10!

Come possiamo vedere, molte tecnologie hanno collaborato per rendere la replica logica una realtà:archiviazione WAL, warm/hot standby, replica WAL fisica, background worker, decodifica logica. Supponendo che il lettore abbia familiarità con la maggior parte delle nozioni di replica fisica, parleremo dei componenti di base della replica logica.

Concetti di base sulla replica logica PostgreSQL

Un po' di terminologia:

  • Pubblicazione: Un insieme di modifiche da un insieme di tabelle definito in un database specifico su un server primario di replica fisica. Una pubblicazione può gestire tutto o parte di:INSERT, DELETE, UPDATE, TRUNCATE.
  • Nodo editore: Il server in cui risiede la pubblicazione.
  • Identità replica: Un modo per identificare la riga lato abbonato per AGGIORNAMENTI e CANCELLAZIONI.
  • Abbonamento: Una connessione a un nodo editore e una o più pubblicazioni in esso contenute. Una sottoscrizione utilizza uno slot di replica dedicato nell'editore per la replica. È possibile utilizzare slot di replica aggiuntivi per la fase di sincronizzazione iniziale.
  • Nodo abbonato: Il server in cui risiede l'abbonamento.

La replica logica segue un modello di pubblicazione/sottoscrizione. Uno o più abbonati possono iscriversi a una o più pubblicazioni su un nodo editore. Gli abbonati possono ripubblicare per consentire la replica a catena. La replica logica di una tabella è composta da due fasi:

  • Scattare un'istantanea della tabella sull'editore e copiarla sull'abbonato
  • Applicazione di tutte le modifiche (dallo snapshot) nella stessa sequenza

La replica logica è transazionale e garantisce che l'ordine delle modifiche applicate all'abbonato rimanga lo stesso dell'editore. La replica logica offre molta più libertà rispetto alla replica fisica (binaria), quindi può essere utilizzata in più modi:

  • Replica specifica di un singolo database o di una tabella (non è necessario replicare l'intero cluster)
  • Impostazione di trigger per l'abbonato per un'attività specifica (come l'anonimato, che è un argomento piuttosto caldo dopo l'entrata in vigore del GDPR)
  • Il fatto che un nodo abbonato raccolga dati da molti nodi editore, consentendo così un'elaborazione analitica centrale
  • Replica tra diverse versioni/architetture/piattaforme (zero downtime upgrade)
  • Utilizzo del nodo dell'abbonato come server di database per un ambiente di test/sviluppo. Il motivo per cui lo vogliamo è perché il test su dati reali è il tipo di test più realistico.

Avvertenze e restrizioni

Ci sono alcune cose che dobbiamo tenere a mente quando si utilizza la replica logica, alcune di esse potrebbero influenzare alcune decisioni di progettazione, ma altre possono portare a incidenti critici.

Restrizioni

  • Sono supportate solo le operazioni DML. Nessun DDL. Lo schema deve essere definito in anticipo
  • Le sequenze non vengono replicate
  • Gli oggetti grandi non vengono replicati
  • Sono supportate solo tabelle di base semplici (le viste materializzate, le tabelle radice delle partizioni, le tabelle esterne non sono supportate)

Avvertenze

Il problema di base che prima o poi dovremo affrontare quando si utilizza la replica logica sono i conflitti sull'abbonato. L'abbonato è un normale server di lettura/scrittura che può fungere da primario in una configurazione di replica fisica o anche da editore in una configurazione di replica logica a cascata. Finché vengono eseguite le scritture sulle tabelle sottoscritte, potrebbero esserci conflitti . Si verifica un conflitto quando i dati replicati violano un vincolo sulla tabella a cui vengono applicati. Di solito, l'operazione che causa questo è INSERT, DELETES o UPDATES che non ha alcun effetto a causa di righe mancanti non causerà un conflitto. Quando si verifica un conflitto, la replica si interrompe. Il ruolo di lavoro logico in background verrà riavviato nell'intervallo specificato (wal_retrieve_retry_interval), tuttavia, la replica avrà esito negativo fino a quando la causa del conflitto non sarà risolta. Questa è una condizione critica che deve essere affrontata immediatamente. In caso contrario, lo spazio di replica bloccato nella sua posizione attuale, il nodo publisher inizierà ad accumulare WAL e inevitabilmente il nodo publisher esaurirà lo spazio su disco . Un conflitto è il motivo più comune per cui la replica potrebbe interrompersi, ma qualsiasi altra condizione errata avrà lo stesso effetto:ad es. abbiamo aggiunto una nuova colonna NOT NULL su una tabella sottoscritta ma ci siamo dimenticati di definire un valore predefinito, oppure abbiamo aggiunto una colonna su una tabella pubblicata ma ci siamo dimenticati di definirla sulla tabella sottoscritta, o abbiamo commesso un errore sul suo tipo e i due tipi non lo sono compatibile. Tutti questi errori interromperanno la replica. Esistono due modi per risolvere un conflitto:

  1. Risolvi il vero problema
  2. Salta la transazione non riuscita chiamando pg_replication_origin_advance

Soluzione b. come mostrato anche qui può essere pericoloso e complicato in quanto è fondamentalmente un processo per tentativi ed errori, e se si sceglie l'LSN corrente sull'editore, potrebbe facilmente ritrovarsi con un sistema di replica rotto poiché potrebbero esserci operazioni tra l'LSN problematico e l'attuale LSN che vorremmo mantenere. Quindi il modo migliore è risolvere effettivamente il problema dal lato dell'abbonato. Per esempio. se si verifica una violazione della CHIAVE UNICA, potremmo aggiornare i dati sull'abbonato o semplicemente eliminare la riga. In un ambiente di produzione, tutto questo deve essere automatizzato o almeno semi automatizzato.

Configurazione dei nodi editore e abbonato

Per una panoramica generale della replica logica in pratica, leggi questo blog.

I parametri rilevanti per la replica logica sono:

  • Lato editore
    • livello_wal>=“logico”
    • max_replication_slots>=#sottoscrizioni + sincronizzazione iniziale della tabella
    • max_wal_senders>=max_replication_slots + other_physical_standby
  • Lato abbonato
    • max_replication_slots>=#abbonamenti
    • max_logical_replication_workers>=#sottoscrizioni + sincronizzazione iniziale della tabella
    • max_worker_processes>=max_logical_replication_workers + 1 + max_parallel_workers

Ci concentreremo sulle considerazioni speciali che derivano dal nostro scopo speciale che abbiamo bisogno della replica logica per raggiungere:creare un cluster di database di test per l'uso da parte del reparto di test . La pubblicazione può essere definita per tutte le tabelle o tabella per tabella. Suggerisco l'approccio tabella per tabella in quanto ci offre la massima flessibilità. I passaggi generali possono essere riassunti come segue:

  • Esegui un nuovo initdb sul nodo abbonato
  • Esegui il dump dello schema del cluster del publisher e copialo nel nodo dell'abbonato
  • Crea lo schema sull'abbonato
  • Decidi quali tavoli ti servono e quali non ti servono.

Per quanto riguarda il punto elenco sopra, ci sono due motivi per cui potresti non aver bisogno di una tabella da replicare o configurare per la replica:

  • È un tavolo fittizio senza importanza (e forse dovresti anche eliminarlo dalla produzione)
  • è una tabella locale nell'ambiente di produzione, il che significa che ha perfettamente senso che la stessa tabella nell'ambiente di test (sottoscrittore) abbia i propri dati

Tutte le tabelle che partecipano alla replica logica devono avere una REPLICA IDENTITY. Questa è per impostazione predefinita la CHIAVE PRIMARIA e, se non disponibile, è possibile definire una chiave UNICA. Passaggio successivo per trovare lo stato delle tabelle rispetto a REPLICA IDENTITY.

  • Trova le tabelle senza un candidato ovvio per REPLICA IDENTITY
    select table_schema||'.'||table_name from information_schema.tables where table_type='BASE TABLE' AND table_schema||'.'||table_name NOT IN (select table_schema||'.'||table_name from information_schema.table_constraints WHERE constraint_type in ('PRIMARY KEY','UNIQUE')) AND table_schema NOT IN ('information_schema','pg_catalog') ;
  • Trova le tabelle senza CHIAVE PRIMARIA ma con un INDICE UNICO
    select table_schema||'.'||table_name from information_schema.table_constraints WHERE constraint_type = 'UNIQUE' EXCEPT select table_schema||'.'||table_name from information_schema.table_constraints WHERE constraint_type = 'PRIMARY KEY';
  • Scorri gli elenchi di cui sopra e decidi cosa fare con ogni tabella
  • Crea la pubblicazione con le tabelle per le quali esiste una PK
    select 'CREATE PUBLICATION data_for_testdb_pub FOR TABLE ONLY ' || string_agg(qry.tblname,', ONLY ') FROM (select table_schema||'.'||quote_ident(table_name) as tblname from information_schema.tables where table_type='BASE TABLE' AND table_schema||'.'||table_name IN (select table_schema||'.'||table_name from information_schema.table_constraints WHERE constraint_type in ('PRIMARY KEY')) AND table_schema NOT IN( 'information_schema','pg_catalog')  ORDER BY 1) as qry;
    \gexec
  • Quindi crea la sottoscrizione sul nodo abbonato
    create subscription data_for_testdb_pub CONNECTION 'dbname=yourdb host=yourdbhost user=repmgr' PUBLICATION data_for_testdb_pub ;
    Anche quanto sopra copierà i dati.
  • Aggiungi le tabelle che desideri che abbiano un indice UNICO
    Esegui sia nei nodi publisher che Subscriber, ad esempio:
    ALTER TABLE someschema.yourtable REPLICA IDENTITY USING INDEX yourindex_ukey;
    Sull'editore:
    ALTER PUBLICATION data_for_testdb_pub ADD TABLE ONLY someschema.yourtable;
    Sull'abbonato:
    ALTER SUBSCRIPTION data_for_testdb_pub REFRESH PUBLICATION WITH ( COPY_DATA );
  • A questo punto (sincronizzazione) dovresti sempre tenere d'occhio il log di PostgreSQL sul nodo dell'abbonato. Non si desidera alcun errore o altro (timeout) che impedisca la continuazione della replica logica. RISOLVI IMMEDIATAMENTE QUALSIASI ERRORE o l'editore continuerà ad accumulare file WAL in pg_wal e alla fine esaurirà lo spazio. Quindi devi fare i conti con
    • Tutti gli ERRORI o qualsiasi messaggio relativo al lavoro logico che risulta in uscita
    • Prenditi cura anche di
      • wal_receiver_timeout
      • wal_sender_timeout

Dopo aver risolto tutti i problemi, dovresti avere il tuo nodo abbonato in esecuzione felicemente. Quindi la prossima domanda è come usarlo come server di database di prova. Dovrai affrontare questi problemi/problemi:

  1. Anonimizzazione
  2. Chiavi primarie e chiavi univoche basate su violazioni di sequenze
  3. Un insieme generale di buone pratiche
  4. Monitoraggio

Anonimizzazione

Per quanto riguarda l'anonimizzazione dei dati personali imposta dal GDPR nell'UE, dovresti scrivere SEMPRE alcuni trigger che cancellano tutti i campi relativi a indirizzi, conti bancari, stato civile, numeri di telefono, e-mail, ecc. Dovresti consultare il tuo responsabile della sicurezza della tua azienda in merito cosa tenere e cosa cancellare. I trigger devono essere definiti come SEMPRE poiché il ruolo di lavoro logico esegue le istruzioni come REPLICA.

Chiavi primarie con sequenze

Per quanto riguarda le sequenze, chiaramente ci sarà un problema con quelle chiavi a meno che non vengano affrontate prima dell'inizio di qualsiasi test. Considera questo caso:

  • Venerdì pomeriggio fai dei test sul database degli iscritti inserendo una nuova riga in qualche tabella. Questo avrà come ID il valore successivo generato dalla sequenza.
  • Vai a casa per il fine settimana.
  • Alcuni utenti di produzione immettono una riga nella stessa tabella nel database dell'editore.
  • La riga verrà replicata in base all'IDENTITÀ REPLICA nel nodo dell'abbonato ma non riuscirà a causa della violazione PK ERROR. Il ruolo di lavoro in background logico uscirà e riproverà. Ma continuerà a fallire finché il problema persiste.
  • La replica si bloccherà. Lo slot di replica inizierà ad accumulare WAL.
  • Il publisher esaurisce lo spazio su disco.
  • Nel fine settimana ricevi un'email che indica che il tuo nodo primario è andato in panico!

Quindi, per risolvere il problema della sequenza, puoi adottare il seguente approccio:

select 'SELECT setval(''' || seqrelid::regclass||''','||CASE WHEN seqincrement <0 THEN -214748364 ELSE 214748364 END||');' from pg_sequence where seqtypid=20;
\gexec

Quello che fa sopra è impostare le sequenze su un valore sufficientemente grande in modo che non si sovrappongano mai per una finestra abbastanza grande in futuro, consentendoti di avere un server di prova senza problemi.

Un insieme di buone pratiche

Dovresti davvero dire ai tuoi programmatori di rendere i loro test non persistenti. Pertanto, qualsiasi test dopo il completamento dovrebbe lasciare il database nello stesso stato in cui era prima del test. Con gli inserimenti di ID basati su sequenza questo non è un problema, abbiamo visto in precedenza una soluzione. Ma con chiavi UNIQUE non di sequenza (ad esempio composte) che potrebbero essere un problema. Quindi è meglio eliminare quei dati di test prima che qualche riga di produzione con lo stesso valore raggiunga la tabella sottoscritta.

Qui dovremmo anche aggiungere la gestione delle modifiche allo schema. Tutte le modifiche allo schema devono essere eseguite anche sull'abbonato per non interrompere il traffico DML replicato.

Scarica il whitepaper oggi Gestione e automazione di PostgreSQL con ClusterControlScopri cosa devi sapere per distribuire, monitorare, gestire e ridimensionare PostgreSQLScarica il whitepaper

Monitoraggio

Dovresti davvero investire in una buona soluzione di monitoraggio. Dovresti monitorare...

All'abbonato:

  • TUTTI i messaggi nel registro dell'abbonato rilevanti per l'uscita logica del lavoratore. L'installazione di uno strumento come tail_n_mail può davvero aiutare in questo. Una configurazione nota per funzionare:
    INCLUDE: ERROR:  .*publisher.*
    INCLUDE: ERROR:  .*exited with exit.*
    INCLUDE: LOG:  .*exited with exit.*
    INCLUDE: FATAL:  
    INCLUDE: PANIC:
    Una volta ricevuto un avviso proveniente da tail_n_mail, dovremmo risolvere immediatamente il problema.
  • pg_stat_abbonamento. Pid non dovrebbe essere nullo. Anche il ritardo dovrebbe essere piccolo.

Presso l'editore:

  • pg_stat_replica. Questo dovrebbe avere tante righe quante dovrebbero essere:una per ogni standby di replica streaming connesso (inclusi nodi di abbonato e altri standby fisici).
  • pg_replication_slots per lo slot dell'abbonato. Questo dovrebbe essere attivo.

In genere, ci vuole del tempo prima che il tuo server database di test ideale funzioni senza problemi, ma una volta che li avrai risolti tutti, i tuoi programmatori ti ringrazieranno per averlo!