Tra le novità introdotte da PostgreSQL 10 sicuramente una delle più importanti è l’implementazione della replica logica nel core di Postgres.
Molti di voi avranno già provato o almeno sentito parlare di pglogical
, estensione di PostgreSQL che fornisce il supporto per la replica logica, che, grazie al logical decoding dei WAL (presente dalla 9.4), permette di replicare una selezione di tabelle da un database ad un altro.
In questo articolo vedremo come adesso questo è possibile con PostgreSQL 10 e quali sono le differenze con pglogical
. Inoltre, viene assunto che il lettore abbia una preparazione di base sulle nozioni di amministrazione di PostgreSQL e di manipolazione dei file di configurazione e di sicurezza.
La replica logica si basa sull’architettura Pubblicazione/Sottoscrizione, si differenziano quindi due ruoli: un publisher
e un subscriber
. A differenza di quella fisica nella quale vengono replicate tutte le modifiche a livello binario, la replica logica permette di filtrare i cambiamenti da replicare scegliendo le tabelle e una combinazione di operazioni su di esse tra UPDATE
, INSERT
e/o DELETE
. Per esempio è possibile replicare solo le UPDATE
di una singola tabella da un server ad un altro a scopo di analisi e/o di aggregazione dei dati.
Sul publisher
viene definita una “pubblicazione”, ovvero una selezione delle tabelle (o tutte) di un database a cui vengono associate le operazioni da replicare ai subscriber
. Grazie alla decodifica logica dei WAL le operazioni sono ricostruite traducendo il codice binario e vengono selezionate solo quelle definite nella “pubblicazione” per essere trasmesse ai subscriber
.
L’installazione di default predispone già impostate alcune delle opzioni necessarie a configurare la replica logica.
Per quanto riguarda il publisher
:
max_replication_slots = 10
subscriber
più alcuni per l’inizializzazione delle tabelle.max_wal_senders = 10
replication_slot
più quelli necessari per le repliche fisiche (es.: Barman).Per quanto riguarda il subscriber
:
max_logical_replication_workers = 4
max_worker_processes = 10
replication workers
più uno.[NOTA] Queste però non bastano per attivare la replica logica. Quello che manca, infatti, sono le informazioni all’interno del codice dei WAL necessarie per ricostruire le operazioni da filtrare e inviare ai
subscriber
. Per ottenere queste informazioni è necessario impostare sulpublisher
il parametrowal_level
alogical
e avviare (o riavviare) il servizio.
L’unico cambiamento che dobbiamo apportare è il seguente parametro sul publisher
pippo-pg10
:
wal_level = logical
I valori preimpostati sono più che sufficienti per questo test. Tuttavia, va tenuto conto che sono parametri che vengono valorizzati all’avvio del servizio e quindi ogni ulteriore cambiamento viene applicato solo dopo un nuovo riavvio. In questa ottica è consigliabile impostare valori adeguati al numero di pubblicazioni e sottoscrizioni previsti, prima di dover riavviare il servizio in produzione.
Consideriamo un caso reale di utilizzo della replica logica, prendendo di esempio un cluster con due server PostgreSQL 10:
pippo-pg10
, ip: 192.168.42.110, publisher
paperino-pg10
, ip: 192.168.42.210, subscriber
Sul server pippo-pg10
sono presenti le tabelle che devono essere replicate su un database del server paperino-pg10
, precedentemente create con pgbench
, un tool molto utile per misurare le prestazioni di PostgreSQL.
psql (10beta4)
Type "help" for help.
postgres=# \c pgbench
You are now connected to database "pgbench" as user "postgres".
pgbench=# \d
List of relations
Schema | Name | Type | Owner
--------+------------------+-------+------------
public | pgbench_accounts | table | user_bench
public | pgbench_branches | table | user_bench
public | pgbench_history | table | user_bench
public | pgbench_tellers | table | user_bench
(4 rows)
A causa delle restrizioni sulla replicazione non è possibile replicare la definizione di una tabella tanto meno di un database. Per questo occorre replicare manualmente la definizione sul server di sottoscrizione, per esempio partendo da un dump dello schema del database pgbench
dal publisher
:
Quindi creare l’utente e il database di destinazione nell’istanza PostgreSQL di paperino-pg10
(operazioni assenti nello script appena estratto):
[root@paperino-pg10 ~]# sudo -iu postgres
-bash-4.2$ createuser user_bench
-bash-4.2$ createdb pgbench -O user_bench
[NOTA] Non è necessario che l’utente e il database abbiano lo stesso nome, ma in questo caso si utilizza lo stesso nome per semplicità.
Adesso è possibile applicare lo script SQL sul server paperino-pg10
:
-bash-4.2$ psql -d pgbench < pgbench.sql
SET
SET
SET
SET
SET
SET
SET
SET
SET
CREATE EXTENSION
COMMENT
SET
SET
SET
CREATE TABLE
ALTER TABLE
CREATE TABLE
ALTER TABLE
CREATE TABLE
ALTER TABLE
CREATE TABLE
ALTER TABLE
ALTER TABLE
ALTER TABLE
ALTER TABLE
Nell’istanza pippo-pg10
è necessario creare il ruolo dedicato alla replicazione e definire opportunamente la regola nel pg_hba.conf
che permetta la connessione dal subscriber paperino-pg10
.
Una pubblicazione è definita come un set di cambiamenti su una o più tabelle appartenenti ad un solo database. Si possono creare più pubblicazioni in un solo database scegliendo di replicare una tra le operazioni di INSERT
, UPDATE
e DELETE
o una loro combinazione. Ogni tabella può appartenere a più pubblicazioni e una singola pubblicazione può avere più sottoscrizioni.
In questo esempio assumiamo di voler replicare tutte le operazioni di modifica su tutte le tabelle di un database. Per creare questo tipo di pubblicazione è necessario essere superutenti (maggiori dettagli):
Una sottoscrizione viene definita su un nodo PostgreSQL, chiamato subscriber
, con una connection string verso il server publisher
e una lista di una o più pubblicazioni alle quali sottoscriversi. Il nome della sottoscrizione viene trasmesso al publisher
come riconoscimento della connessione se non è definito il campo application_name
nella stringa di connessione.
Per creare una sottoscrizione è necessario essere superutenti (maggiori dettagli).
Su paperino-pg10
:
pgbench=# CREATE SUBSCRIPTION pgbench_rep CONNECTION 'dbname=pgbench host=192.168.42.110 user=replica' PUBLICATION pgbench_alltables;
NOTICE: created replication slot "pgbench_rep" on publisher
CREATE SUBSCRIPTION
[NOTA] Un replication slot viene creato per ogni sottoscrizione, più uno temporaneo per la sincronizzazione iniziale dei dati.
Adesso è possibile controllare che l’inizializzazione dei dati nel subscriber
rispecchi la situazione nel publisher
:
sul publisher
pippo-pg10
postgres=# \c pgbench
You are now connected to database "pgbench" as user "postgres".
pgbench=# select * from pg_publication;
pubname | pubowner | puballtables | pubinsert | pubupdate | pubdelete
-------------------+----------+--------------+-----------+-----------+-----------
pgbench_alltables | 10 | t | t | t | t
(1 row)
pgbench=# select * from pgbench_branches limit 5;
bid | bbalance | filler
-----+----------+--------
1 | 0 |
2 | 0 |
3 | 0 |
4 | 0 |
5 | 0 |
(5 rows)
pgbench=# select * from pgbench_tellers limit 5;
tid | bid | tbalance | filler
-----+-----+----------+--------
1 | 1 | 0 |
2 | 1 | 0 |
3 | 1 | 0 |
4 | 1 | 0 |
5 | 1 | 0 |
(5 rows)
e sul subscriber
paperino-pg10
postgres=# \c pgbench
You are now connected to database "pgbench" as user "postgres".
pgbench=# select * from pg_subscription;
subdbid | subname | subowner | subenabled | subconninfo | subslotname | subsynccommit | subpublications
---------+-------------+----------+------------+-------------------------------------------------+-------------+---------------+---------------------
16532 | pgbench_rep | 10 | t | dbname=pgbench host=192.168.42.110 user=replica | pgbench_rep | off | {pgbench_alltables}
(1 row)
pgbench=# select * from pgbench_branches limit 5;
bid | bbalance | filler
-----+----------+--------
1 | 0 |
2 | 0 |
3 | 0 |
4 | 0 |
5 | 0 |
(5 rows)
pgbench=# select * from pgbench_tellers limit 5;
tid | bid | tbalance | filler
-----+-----+----------+--------
1 | 1 | 0 |
2 | 1 | 0 |
3 | 1 | 0 |
4 | 1 | 0 |
5 | 1 | 0 |
(5 rows)
Le tabelle sul server paperino-pg10
sono state inizializzate!
Il test consiste nel verificare che le modifiche apportate alle tabelle sul server pippo-pg10
siano effettivamente replicate su paperino-pg10
.
Su pippo-pg10
:
pgbench=# BEGIN;
BEGIN
pgbench=# SELECT * FROM pgbench_accounts WHERE aid = 1;
aid | bid | abalance | filler
-----+-----+----------+--------------------------------------------------------------------------------------
1 | 1 | 0 |
(1 row)
pgbench=# UPDATE pgbench_accounts SET filler = 'Jonny was here' WHERE aid = 1;
UPDATE 1
pgbench=# select * from pgbench_accounts where aid = 1;
aid | bid | abalance | filler
-----+-----+----------+--------------------------------------------------------------------------------------
1 | 1 | 0 | Jonny was here
(1 row)
pgbench=# COMMIT;
COMMIT
pgbench=#
Una volta eseguito il COMMIT
nel WAL viene scritta l’operazione UPDATE
appena eseguita e replicata al subscriber
:
pgbench=# select * from pgbench_accounts where aid = 1;
aid | bid | abalance | filler
-----+-----+----------+--------------------------------------------------------------------------------------
1 | 1 | 0 | Jonny was here
(1 row)
Attenzione! Le operazioni di UPDATE e DELETE sono replicate al
subscriber
solo sulle tabelle che possiedono una REPLICA IDENTITY, che di default è lachiave primaria
. La tabellahistory
dipgbench
non sarebbe stata replicata in caso avessimo creato una pubblicazione limitata alle UPDATE e/o DELETE, perché non possiede una chiave primaria.
La replica logica a differenza della replica fisica di un’istanza in hot-standby
mode, non è da considerarsi un’architettura Master/Standby in quanto il subscriber
è egli stesso un master, permettendo quindi le scritture su tutte le tabelle. Può essere comunque utilizzata in maniera simile usando utenti con permessi di sola lettura sul subscriber
fino al prossimo switchover.
Per l’architettura multi-master, nella quale le modifiche vengono effettuate concorrentemente su più nodi, si rimanda alla lettura del sistema BDR.
Questa tecnologia permette di migrare da una major version di PostgreSQL ad un’altra, al contrario della replica fisica a causa dell’incompatibilità binaria tra differenti versioni.
L’implementazione nel core di PostgreSQL 10 della replica logica è un’eredità del progetto pglogical
, che, essendo più maturo, possiede molte funzionalità in più. Tra le caratteristiche più importanti di pglogical
vi è la possibilità di replicare le modifiche allo schema di un database e le DDL, quindi le definizioni delle tabelle. Inoltre è possibile configurare la risoluzione dei conflitti, mentre con PostgreSQL 10, in caso di conflitto la replica logica si interrompe ed è necessario l’intervento umano per la sua risoluzione. Un’altra funzionalità che rende pglogical
molto potente è la possibilità di replicare specifiche righe e/o colonne di una tabella.
L’inizializzazione dei dati nelle tabelle del subscriber
è una funzionalità molto utile, ma è possibile scegliere di non effettuarla durante la creazione della sottoscrizione.
Anche la replica logica può essere sottoposta alle stesse configurazioni di sincronizzazione delle transazioni tra standby e master, in modo da avere una perdita dati zero. Sono sufficienti gli stessi parametri della replica fisica per ottenere la sincronizzazione tra master e standby.
Nell’esempio di questo articolo, per attivare la replica logica sincrona è sufficiente configurare i seguenti parametri:
e riavviare PostgreSQL.
Il valore del parametro
synchronous_standby_name
corrisponde al nome della sottoscrizione. Maggiori dettagli sulla configurazione del parametrosynchronous_standby_names
.
Adesso mi rimane una domanda: qual è la differenza di velocità fra la replica fisica e quella logica? Potrebbe essere l’interessante argomento del mio prossimo articolo.
This Post Has 0 Comments