Поскольку в нашей конфигурации не будет архива журнала предзаписи, важно на всех этапах использовать слот репликации - иначе при определенной задержке мастер может успеть удалить необходимые сегменты и весь процесс придется повторять с самого начала.
Создаем слот:
α=> SELECT pg_create_physical_replication_slot('replica');
pg_create_physical_replication_slot ------------------------------------- (replica,) (1 row)
Вначале слот не инициализирован (restart_lsn пустой):
α=> SELECT * FROM pg_replication_slots \gx
-[ RECORD 1 ]-------+--------- slot_name | replica plugin | slot_type | physical datoid | database | temporary | f active | f active_pid | xmin | catalog_xmin | restart_lsn | confirmed_flush_lsn |
Как мы помним из модуля "Резервное копирование", в версии 10+ все необходимые настройки есть по умолчанию:
Создадим автономную резервную копию, используя созданный слот. Копию расположим в каталоге PGDATA будущей реплики.
postgres$ rm -rf /var/lib/postgresql/10/beta/*
postgres$ pg_basebackup -U student --pgdata=/var/lib/postgresql/10/beta -R --slot=replica
Возможность использовать слот появилась у pg_basebackup, начиная с версии 9.6. В более ранних версиях следовало бы заранее инициализировать слот, подключившись к нему утилитой pg_receive_xlog.
После выполнения резервной копии слот инициализировался, и мастер хранит все файлы журнала с начала копирования (restart_lsn).
α=> SELECT * FROM pg_replication_slots \gx
-[ RECORD 1 ]-------+---------- slot_name | replica plugin | slot_type | physical datoid | database | temporary | f active | f active_pid | xmin | catalog_xmin | restart_lsn | 0/8000000 confirmed_flush_lsn |
Мы собираемся запустить реплику одновременно с основным сервером на одной машине, поэтому в настройках необходимо поменять порт:
postgres$ echo 'port = 5433' >> /var/lib/postgresql/10/beta/postgresql.auto.conf
Чтобы реплика выполняла запросы (была "горячей"), необходимо выставить параметр hot_standby. Если этого не сделать, реплика будет "теплой" и не будет принимать подключения.
postgres$ echo 'hot_standby = on' >> /var/lib/postgresql/10/beta/postgresql.auto.conf
Файл recovery.conf был подготовлен утилитой pg_basebackup, и уже включает указание режима непрерывного восстановления (standby_mode) и слота репликации (primary_slot_name):
postgres$ cat /var/lib/postgresql/10/beta/recovery.conf
standby_mode = 'on' primary_conninfo = 'user=student passfile=''/var/lib/postgresql/.pgpass'' host=''/var/run/postgresql'' port=5432 sslmode=prefer sslcompression=1 krbsrvname=postgres target_session_attrs=any' primary_slot_name = 'replica'
Можно запускать реплику.
Журнальные записи, необходимые для восстановления согласованности, реплика получит от мастера по протоколу репликации. Далее она войдет в режим непрерывного восстановления, как указано в recovery.conf, и будет продолжать получать и проигрывать поток записей.
student$ sudo pg_ctlcluster 10 beta start
Посмотрим на процессы реплики.
postgres$ ps -o pid,command --ppid `head -n 1 /var/lib/postgresql/10/beta/postmaster.pid`
PID COMMAND 1427 postgres: 10/beta: startup process waiting for 000000010000000000000009 1428 postgres: 10/beta: checkpointer process 1429 postgres: 10/beta: writer process 1430 postgres: 10/beta: stats collector process 1431 postgres: 10/beta: wal receiver process
Процесс wal receiver принимает поток журнальных записей, процесс startup применяет изменения.
Процессы wal writer и autovacuum launcher отсутствуют.
И сравним с процессами мастера.
postgres$ ps -o pid,command --ppid `head -n 1 /var/lib/postgresql/10/alpha/postmaster.pid`
PID COMMAND 1035 postgres: 10/alpha: checkpointer process 1036 postgres: 10/alpha: writer process 1037 postgres: 10/alpha: wal writer process 1038 postgres: 10/alpha: autovacuum launcher process 1039 postgres: 10/alpha: stats collector process 1040 postgres: 10/alpha: bgworker: logical replication launcher 1090 postgres: 10/alpha: student student [local] idle 1432 postgres: 10/alpha: wal sender process student [local] idle
Здесь добавился процесс wal sender, обслуживающий подключение по протоколу репликации.
Выполним несколько команд на мастере:
α=> CREATE DATABASE replica_physical;
CREATE DATABASE
α=> \c replica_physical;
You are now connected to database "replica_physical" as user "student".
α=> CREATE TABLE test(s text);
CREATE TABLE
α=> INSERT INTO test VALUES ('Привет, мир!');
INSERT 0 1
Проверим реплику:
student$ psql -p 5433 -d replica_physical
β=> SELECT * FROM test;
s -------------- Привет, мир! (1 row)
При этом изменения на реплике не допускаются:
β=> INSERT INTO test VALUES ('Replica');
ERROR: cannot execute INSERT in a read-only transaction
Вообще реплику от мастера можно отличить с помощью функции:
β=> SELECT pg_is_in_recovery();
pg_is_in_recovery ------------------- t (1 row)
Состояние репликации можно смотреть в специальном представлении на мастере. Чтобы пользователь получил доступ к этой информации, ему должна быть выдана роль pg_read_all_stats (или он должен быть суперпользователем).
α=> \c - postgres
You are now connected to database "replica_physical" as user "postgres".
α=> GRANT pg_read_all_stats TO student;
GRANT ROLE
α=> \c - student
You are now connected to database "replica_physical" as user "student".
α=> SELECT * FROM pg_stat_replication \gx
-[ RECORD 1 ]----+------------------------------ pid | 1432 usesysid | 16384 usename | student application_name | walreceiver client_addr | client_hostname | client_port | -1 backend_start | 2018-06-13 19:59:13.044052+03 backend_xmin | state | streaming sent_lsn | 0/9025620 write_lsn | 0/9025620 flush_lsn | 0/9025620 replay_lsn | 0/9025620 write_lag | 00:00:00.000199 flush_lag | 00:00:00.001131 replay_lag | 00:00:00.001184 sync_priority | 0 sync_state | async
Обратите внимание на поля *_lsn (и *_lag) - они показывают отставание реплики на разных этапах. Сейчас все позиции совпадают, отставание нулевое.
Теперь вставим в таблицу большое количество строк, чтобы увидеть репликацию в процессе работы.
α=> INSERT INTO test SELECT 'Just a line' FROM generate_series(1,1000000);
INSERT 0 1000000
α=> SELECT *, pg_current_wal_lsn() from pg_stat_replication \gx
-[ RECORD 1 ]------+------------------------------ pid | 1432 usesysid | 16384 usename | student application_name | walreceiver client_addr | client_hostname | client_port | -1 backend_start | 2018-06-13 19:59:13.044052+03 backend_xmin | state | streaming sent_lsn | 0/D1299E0 write_lsn | 0/D1299E0 flush_lsn | 0/D1299E0 replay_lsn | 0/C47A7FC write_lag | 00:00:00.135193 flush_lag | 00:00:00.143305 replay_lag | 00:00:00.743007 sync_priority | 0 sync_state | async pg_current_wal_lsn | 0/D1299E0
Видно, что возникла небольшая задержка. Однако не следует рассчитывать на точность выше чем полсекунды, так как статистика обновляется примерно с такой частотой.
Проверим реплику:
β=> SELECT count(*) FROM test;
count --------- 1000001 (1 row)
Все строки успешно доехали.
И еще раз проверим состояние репликации:
α=> SELECT *, pg_current_wal_lsn() from pg_stat_replication \gx
-[ RECORD 1 ]------+------------------------------ pid | 1432 usesysid | 16384 usename | student application_name | walreceiver client_addr | client_hostname | client_port | -1 backend_start | 2018-06-13 19:59:13.044052+03 backend_xmin | state | streaming sent_lsn | 0/D1299E0 write_lsn | 0/D1299E0 flush_lsn | 0/D1299E0 replay_lsn | 0/D1299E0 write_lag | 00:00:00.135193 flush_lag | 00:00:00.143305 replay_lag | 00:00:00.54367 sync_priority | 0 sync_state | async pg_current_wal_lsn | 0/D1299E0
Все позиции выровнялись.
Конец демонстрации.