Физическая репликация. Резервная копия мастера

Начиная с версии 10, все необходимые настройки уже присутствуют по умолчанию:

Создадим автономную резервную копию. В качестве каталога для сохранения сразу используем PGDATA резервного сервера. Ключ -R создаст заготовку управляющего файла recovery.conf.

postgres$ rm -rf /usr/local/pgsql2/data/*
postgres$ pg_basebackup --pgdata=/usr/local/pgsql2/data -R

Резервный сервер уже предварительно собран и установлен.


Реплика

Мы собираемся запустить второй сервер одновременно с основным на одной машине, поэтому в настройках необходимо поменять порт:

postgres$ echo 'port = 5433' >> /usr/local/pgsql2/data/postgresql.auto.conf

И добавим параметр горячего резерва, чтобы реплика принимала запросы на чтение:

postgres$ echo 'hot_standby = on' >> /usr/local/pgsql2/data/postgresql.auto.conf

Заготовка для recovery.conf была подготовлена утилитой pg_basebackup, и она нас устраивает:

postgres$ cat /usr/local/pgsql2/data/recovery.conf
standby_mode = 'on'
primary_conninfo = 'user=postgres passfile=''/home/postgres/.pgpass'' port=5432 sslmode=disable sslcompression=1 target_session_attrs=any'

Можно запускать сервер.

postgres$ pg_ctl -w -l /home/postgres/logfile2 -D /usr/local/pgsql2/data start
waiting for server to start.... done
server started

В приведенной команде мы явно указываем расположение каталога с данными кластера. Для удобства в виртуальной машине есть команды "1" и "2", которые переключают окружение на первый и на второй сервер соответственно.


Посмотрим на процессы реплики.

postgres$ ps -o pid,command --ppid `head -n 1 /usr/local/pgsql2/data/postmaster.pid`
  PID COMMAND
 4316 postgres: startup process   waiting for 000000010000000000000008
 4317 postgres: checkpointer process   
 4318 postgres: writer process   
 4319 postgres: stats collector process   
 4320 postgres: wal receiver process   

Процесс wal receiver принимает поток журнальных записей, процесс startup применяет изменения.


И сравним с процессами мастера.

postgres$ ps -o pid,command --ppid `head -n 1 /usr/local/pgsql/data/postmaster.pid`
  PID COMMAND
 4209 postgres: postgres postgres [local] idle
 4321 postgres: wal sender process postgres [local] idle
22611 postgres: checkpointer process   
22612 postgres: writer process   
22613 postgres: wal writer process   
22614 postgres: autovacuum launcher process   
22615 postgres: stats collector process   
22616 postgres: bgworker: logical replication launcher   

Здесь добавился процесс wal sender.


Проверка репликации

Состояние репликации можно смотреть в специальном представлении на мастере:

postgres$ psql -p 5432
=> SELECT * FROM pg_stat_replication \gx
-[ RECORD 1 ]----+-----------------------------
pid              | 4321
usesysid         | 10
usename          | postgres
application_name | walreceiver
client_addr      | 
client_hostname  | 
client_port      | -1
backend_start    | 2019-03-31 15:26:16.48161+03
backend_xmin     | 
state            | streaming
sent_lsn         | 0/8000000
write_lsn        | 0/8000000
flush_lsn        | 0/8000000
replay_lsn       | 0/8000000
write_lag        | 00:00:00.101554
flush_lag        | 00:00:00.101554
replay_lag       | 00:00:00.101554
sync_priority    | 0
sync_state       | async


Выполним несколько команд на мастере:

=> CREATE DATABASE replica_overview;
CREATE DATABASE
=> \c replica_overview;
You are now connected to database "replica_overview" as user "postgres".
=> CREATE TABLE test(id integer PRIMARY KEY, descr text);
CREATE TABLE

Проверим реплику:

postgres$ psql -p 5433 -d replica_overview
=> SELECT * FROM test;
 id | descr 
----+-------
(0 rows)


=> INSERT INTO test VALUES (1, 'Раз');
INSERT 0 1

=> SELECT * FROM test;
 id | descr 
----+-------
  1 | Раз
(1 row)

При этом изменения на реплике не допускаются:

=> INSERT INTO test VALUES (2, 'Два');
ERROR:  cannot execute INSERT in a read-only transaction

Логическая репликация

Теперь переведем реплику из режима восстановления в обычный режим. Таким образом, получим два самостоятельных, никак не связанных друг с другом сервера.

postgres$ pg_ctl -w -D /usr/local/pgsql2/data promote
waiting for server to promote.... done
server promoted
=> INSERT INTO test VALUES (2, 'Два');
INSERT 0 1

Далее мы хотим настроить между серверами логическую репликацию. Для этого нам понадобится дополнительная информация в журнале.

=> ALTER SYSTEM SET wal_level = logical;
ALTER SYSTEM
postgres$ pg_ctl -w -l /home/postgres/logfile -D /usr/local/pgsql/data restart
waiting for server to shut down.... done
server stopped
waiting for server to start.... done
server started

На первом сервере создаем публикацию:

postgres$ psql -d replica_overview
=> CREATE PUBLICATION test_pub FOR TABLE test;
CREATE PUBLICATION
=> \dRp+
                Publication test_pub
  Owner   | All tables | Inserts | Updates | Deletes 
----------+------------+---------+---------+---------
 postgres | f          | t       | t       | t
Tables:
    "public.test"


На втором сервере подписываемся на эту публикацию (отключаем первоначальное копирование данных):

=> CREATE SUBSCRIPTION test_sub
CONNECTION 'host=localhost port=5432 user=postgres dbname=replica_overview'
PUBLICATION test_pub WITH (copy_data = false);

NOTICE:  created replication slot "test_sub" on publisher
CREATE SUBSCRIPTION
=> \dRs
            List of subscriptions
   Name   |  Owner   | Enabled | Publication 
----------+----------+---------+-------------
 test_sub | postgres | t       | {test_pub}
(1 row)


=> INSERT INTO test VALUES (3, 'Три');
INSERT 0 1

=> SELECT * FROM test;
 id | descr 
----+-------
  1 | Раз
  2 | Два
  3 | Три
(3 rows)


Состояние подписки можно посмотреть в представлении:

=> SELECT * FROM pg_stat_subscription \gx
-[ RECORD 1 ]---------+------------------------------
subid                 | 24576
subname               | test_sub
pid                   | 4789
relid                 | 
received_lsn          | 0/802E0DC
last_msg_send_time    | 2019-03-31 15:26:20.175076+03
last_msg_receipt_time | 2019-03-31 15:26:20.175447+03
latest_end_lsn        | 0/802E0DC
latest_end_time       | 2019-03-31 15:26:20.175076+03


К процессам сервера добавился logical replication worker (его номер указан в pg_stat_subscription.pid):

postgres$ ps -o pid,command --ppid `head -n 1 /usr/local/pgsql2/data/postmaster.pid`
  PID COMMAND
 4317 postgres: checkpointer process   
 4318 postgres: writer process   
 4319 postgres: stats collector process   
 4543 postgres: postgres replica_overview [local] idle
 4642 postgres: wal writer process   
 4643 postgres: autovacuum launcher process   
 4644 postgres: bgworker: logical replication launcher   
 4789 postgres: bgworker: logical replication worker for subscription 24576  

Если репликация больше не нужна, надо аккуратно удалить подписку - иначе на публикующем сервере останется открытым репликационный слот.

=> DROP SUBSCRIPTION test_sub;
NOTICE:  dropped replication slot "test_sub" on publisher
DROP SUBSCRIPTION

Конец демонстрации.