Уровень 3.0
Предусловия:
- Изучена лекция 6 «Физическая репликация»
Настройка и анализ потоковой репликации
Подготовка виртуальной машины Host1
-
Подключитесь к виртуальной машине с помощью
webssh, указав IP-адрес Host1. -
Запустите оболочку от имени пользователя
postgres:[student@ServerName ~]$ sudo -iu postgres -
Остановите экземпляр Pangolin, включите подсчет контрольных сумм и запустите экземпляр Pangolin:
[postgres@ServerName ~]$ pg_ctl stopwaiting for server to shut down....... doneserver stopped[postgres@ServerName ~]$ pg_checksums --enableChecksum operation completedFiles scanned: 1011Blocks scanned: 3486Files written: 827Blocks written: 3486pg_checksums: syncing data directorypg_checksums: updating control fileChecksums enabled in cluster[postgres@ServerName ~]$ pg_ctl start -l logfilewaiting for server to start.... doneserver startedВ рамках этой лабораторной работы подсчет контрольных сумм нужен для использования утилиты синхронизации данных
pg_rewind. -
В терминале подключитесь в
psqlк базе данныхpostgresот имени пользователяpostgres:[postgres@ServerName ~]$ psqlpsql (15.5)Type "help" for help. -
Создайте роль
replicator:postgres=# CREATE ROLE replicator WITH LOGIN REPLICATION PASSWORD 'replicator';CREATE ROLEУказанная роль будет использоваться для подключения по протоколу репликации, поэтому нужен атрибут
REPLICATION. -
Создайте базу данных
replication_dbи подключитесь к ней:postgres=# CREATE DATABASE replication_db;CREATE DATABASEpostgres=# \c replication_dbYou are now connected to database "replication_db" as user "postgres".
Подготовка виртуальной машины Host2
-
Подключитесь к виртуальной машине посредством
webssh, указав IP-адрес Host2. -
Запустите оболочку от имени пользователя
postgres:[student@ServerName ~]$ sudo -iu postgres -
В домашнем каталоге пользователя
postgresсоздайте каталогreplicaи задайте необходимые права для него:[postgres@ServerName ~]$ mkdir replica && chmod 0700 replicaСозданный каталог будет являться каталогом данных для создаваемой реплики, поэтому его владельцем должен быть пользователь
postgresс правами 0700 (или 0750).
Настройка потоковой репликации
-
В сеансе
psqlна Host1 проверьте значения параметровwal_level,max_wal_sendersиmax_replication_slots:replication_db=# SELECT name, setting FROM pg_settings WHERE name IN ('wal_level', 'max_wal_senders', 'max_replication_slots');name | setting-----------------------+---------max_replication_slots | 10max_wal_senders | 10wal_level | replica(3 rows)Вспомним, что для подключения по протоколу репликации уровень журнала (
wal_level) должен быть не менееreplica.Также должно быть достаточным допустимое количество процессов
wal sender(max_wal_senders), и в случае использования слота репликации — значение параметраmax_replication_slots.Значения по умолчанию указанных параметров позволяют использовать протокол репликации в рамках лабораторной работы.
-
В сеансе
psqlна Host1 проверьте настройки в файлеpg_hba.conf:replication_db=# SELECT line_number, type, database, user_name, address, netmask, auth_method FROM pg_hba_file_rules;line_number | type | database | user_name | address | netmask | auth_method-------------+-------+---------------+-----------+---------------+-----------------------------------------+---------------89 | local | {all} | {all} | | | trust91 | host | {all} | {all} | 172.29.53.165 | 255.255.255.255 | trust92 | host | {all} | {all} | 0.0.0.0 | 0.0.0.0 | scram-sha-25693 | host | {all} | {all} | 127.0.0.1 | 255.255.255.255 | trust95 | host | {all} | {all} | ::1 | ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff | trust98 | local | {replication} | {all} | | | trust99 | host | {replication} | {all} | 127.0.0.1 | 255.255.255.255 | trust100 | host | {replication} | {all} | ::1 | ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff | trust(8 rows)Текущие настройки
pg_hba.confне позволяют подключаться по протоколу репликации через сетевой интерфейс. Исправим это. -
В сеансе
psqlна Host1 добавьте в файлpg_hba.confразрешение для доступа через сетевой интерфейс по протоколу репликации:replication_db=# \! sed -i '99i\host replication all 0.0.0.0/0 scram-sha-256' /pgdata/\{major-minor\}.0/data/pg_hba.confВ этом случае перед 99-й строкой файла
pg_hba.confбыла добавлена строка с разрешением доступа через сетевой интерфейс с аутентификацией по паролю. -
В сеансе
psqlна Host1 проверьте корректность внесенных вpg_hba.confизменений и перечитайте конфигурацию:replication_db=# SELECT line_number, type, database, user_name, address, netmask, auth_method FROM pg_hba_file_rules;line_number | type | database | user_name | address | netmask | auth_method-------------+-------+---------------+-----------+---------------+-----------------------------------------+---------------89 | local | {all} | {all} | | | trust91 | host | {all} | {all} | 172.29.53.165 | 255.255.255.255 | trust92 | host | {all} | {all} | 0.0.0.0 | 0.0.0.0 | scram-sha-25693 | host | {all} | {all} | 127.0.0.1 | 255.255.255.255 | trust95 | host | {all} | {all} | ::1 | ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff | trust98 | local | {replication} | {all} | | | trust99 | host | {replication} | {all} | 0.0.0.0 | 0.0.0.0 | scram-sha-256100 | host | {replication} | {all} | 127.0.0.1 | 255.255.255.255 | trust101 | host | {replication} | {all} | ::1 | ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff | trust(9 rows)replication_db=# SELECT pg_reload_conf();pg_reload_conf----------------t(1 row)Необходимое разрешение успешно добавлено. Для применения новых настроек файлы конфигурации были перечитаны функцией
pg_reload_conf. -
В сеансе
psqlна Host1 создайте слот репликации:replication_db=# SELECT pg_create_physical_replication_slot('replica_slot');pg_create_physical_replication_slot-------------------------------------(replica_slot,)(1 row) -
В сеансе Host2 создайте базовую резервную копию, указав ip-адрес хоста Host1 и введя пароль для роли
replicator:[postgres@ServerName ~]$ pg_basebackup -h 172.29.53.216 -U replicator -D ~/replica -R -S replica_slotPassword:Вспомним, что утилита
pg_basebackupпозволяет сделать базовую резервную копию и подготовить ее для использования в качестве реплики.Для этого с помощью ключа
-Rдается указание о необходимости создания файлаstandby.signalи задания значений для параметровprimary_conninfoиprimary_slot_nameв файлеpostgresql.auto.confКаталог для размещения копии указан помощью ключа
-D.С помощью ключа
-Sуказано имя слота репликации на мастере. -
В сеансе Host2 проверьте содержимое каталога с созданной копией кластера:
[postgres@ServerName ~]$ ls ~/replicabackup_label pg_commit_ts pg_integrity pg_perf_insights pg_replslot pg_stat_tmp PG_VERSION postgresql.confbackup_manifest pg_dynshmem pg_logical pg_pp_cache pg_serial pg_subtrans pg_wal PRODUCT_VERSIONbase pg_hba.conf pg_multixact pg_prep_stats pg_snapshots pg_tblspc pg_xact standby.signalglobal pg_ident.conf pg_notify pg_quota.conf pg_stat pg_twophase postgresql.auto.conf tracingПомимо файлов
standby.signalиpostgresql.auto.confутилита также создала файлbackup_label, где указан LSN, начиная с которого требуется наличие журнальных записей для восстановления. -
В сеансе Host2 посмотрите содержимое файла
postgresql.auto.conf:[postgres@ServerName ~]$ cat ~/replica/postgresql.auto.conf# Do not edit this file manually!# It will be overwritten by the ALTER SYSTEM command.primary_conninfo = 'user=replicator password=replicator channel_binding=prefer host={replica-IP} port=5432 client_encoding=UTF8 sslmode=prefer ssllegacyprovider=1 sslcompression=0 sslcert=''/pg_ssl/client.crt'' sslkey=''/pg_ssl/client.key'' sslrootcert=''/pg_ssl/root.crt'' sslsni=1 ssl_min_protocol_version=TLSv1.2 gssencmode=prefer krbsrvname=postgres target_session_attrs=any'primary_slot_name = 'replica_slot'В файле появилась строка подключения к мастеру (
primary_conninfo) и имя слота (primary_slot_name), через который будут передаваться записи WAL. -
В сеансе Host2 посмотрите содержимое файла
backup_label:[postgres@ServerName ~]$ cat ~/replica/backup_labelSTART WAL LOCATION: 0/2000028 (file 000000010000000000000002)CHECKPOINT LOCATION: 0/2000070BACKUP METHOD: streamedBACKUP FROM: primarySTART TIME: 2025-11-06 19:32:00 UTCLABEL: pg_basebackup base backupSTART TIMELINE: 1Для восстановления из созданной копии требуется записи WAL начиная с LSN = 0/2000028 (
START WAL LOCATION).Сравним это значение с тем, что хранит слот репликации.
-
В сеансе
psqlна Host1 проверьте состояние созданного слота репликации:replication_db=# SELECT * FROM pg_replication_slots WHERE slot_name='replica_slot'\gx-[ RECORD 1 ]-------+-------------slot_name | replica_slotplugin |slot_type | physicaldatoid |database |temporary | factive | factive_pid |xmin |catalog_xmin |restart_lsn | 0/2000000confirmed_flush_lsn |wal_status | reservedsafe_wal_size |two_phase | fСлот репликации не даст контрольной точке удалить необходимые реплике сегменты WAL.
В параметре
restart_lsnслота указана начальная позиция сегмента WAL, в котором есть записи, еще не переданные через слот. -
В сеансе Host2 проверьте содержимое файла
pg_hba.confскопированного кластера:[postgres@ServerName ~]$ tail -n 17 replica/pg_hba.conf# TYPE DATABASE USER ADDRESS METHOD# "local" is for Unix domain socket connections onlylocal all all trust# IPv4 local connections:host all all 172.29.53.216/32 trusthost all all 0.0.0.0/0 scram-sha-256host all all 127.0.0.1/32 trust# IPv6 local connections:host all all ::1/128 trust# Allow replication connections from localhost, by a user with the# replication privilege.local replication all trusthost replication all 0.0.0.0/0 scram-sha-256host replication all 127.0.0.1/32 trusthost replication all ::1/128 trustФайл
pg_hba.confбыл скопирован без изменений.Если реплика будет использоваться для выполнения запросов, то отредактируйте указанный файл.
В рамках этой лабораторной работы подключение к реплике может быть выполнено через localhost (127.0.0.1/32), разрешение для которого в файле указано.
Проверка репликации
-
В сеансе Host2 остановите запущенный экземпляр Pangolin:
[postgres@ServerName ~]$ pg_ctl stopwaiting for server to shut down.... doneserver stopped -
В сеансе Host2 запустите экземпляр Pangolin, указав в параметре
-Dкаталога данных скопированного кластера баз данных:[postgres@ServerName ~]$ pg_ctl start -D /home/postgres/replica -l logfilewaiting for server to start.... doneserver startedПосле запуска подготовленного экземпляра сначала процесс
startupвосстановил согласованность данных, применяя журнальные записи начиная сSTART WAL LOCATION(в файлеbackup_label), затем, не прекращая свою работу, продолжил восстановление, применяя получаемые в мастера журнальные записи. -
В сеансе Host2 подключитесь в
psqlк базе данныхreplication_db:[postgres@ServerName ~]$ psql -d replication_db -h localhostpsql (15.5)Type "help" for help. -
В сеансе
psqlна Host2 проверьте состояние восстановления кластера:replication_db=# SELECT pg_is_in_recovery();pg_is_in_recovery-------------------t(1 row)Функция
pg_is_in_recoveryпозволяет узнать, находится ли экземпляр в состоянии восстановления, то есть является ли он ведомым или репликой.Здесь, как и ожидалось, запущенный экземпляр находится в состоянии восстановления получаемых записей WAL.
Проверим работу репликации.
-
В сеансе
psqlна Host1 создайте простую таблицу и вставьте в нее одну строку:replication_db=# CREATE TABLE some_table(value text);CREATE TABLEreplication_db=# INSERT INTO some_table VALUES('Строка, вставленная на мастере');INSERT 0 1 -
В сеансе
psqlна Host2 проверьте содержимое таблицыsome_table:replication_db=# SELECT * FROM some_table;value--------------------------------Строка, вставленная на мастере(1 row)Созданная таблица и вставленная в нее строка успешно «добрались» с мастера до реплики.
При этом на реплике возможно выполнение запросов, то есть она работает в режиме горячего резерва.
-
В сеансе
psqlна Host2 проверьте значение параметраhot_standby:replication_db=# SHOW hot_standby;hot_standby-------------on(1 row)Действительно, параметр
hot_standbyпо умолчанию включен. -
В сеансе
psqlна Host2 попробуйте вставить строку в таблицуsome_table:replication_db=# INSERT INTO some_table VALUES('Строка, вставленная на реплике');ERROR: cannot execute INSERT in a read-only transactionВ то время как изменения данных на реплике не допускаются.
-
В сеансе
psqlна Host1 проверьте состояние репликации:replication_db=# SELECT * FROM pg_stat_replication\gx-[ RECORD 1 ]----+------------------------------pid | 19513usesysid | 16389usename | replicatorapplication_name | host2client_addr | 172.29.53.58client_hostname |client_port | 51636backend_start | 2025-11-06 20:09:42.646771+00backend_xmin |state | streamingsent_lsn | 0/3024E30write_lsn | 0/3024E30flush_lsn | 0/3024E30replay_lsn | 0/3024E30write_lag |flush_lag |replay_lag |sync_priority | 0sync_state | asyncreply_time | 2025-11-06 21:06:27.534848+00Из представления
pg_stat_replicationна мастере можно получить информацию о текущем состоянии репликации.В частности, в нем для каждой подключенной реплики есть ее адрес (
client_addr), состояние (state) и режим фиксации транзакций (sync_state).Отдельно стоит отметить группу параметров для мониторинга отставания реплики от мастера в различных точках трансляции записей WAL. Параметры с суффиксом
_lsnсодержат LSN последних записей, прошедших через указанные точки, а параметры с суффиксом_lagсодержат временные задержки на различных этапах с момента сохранения записи в сегменте WAL на мастере.
Настройка синхронного режима
-
В сеансе
psqlна Host2 задайте значение параметраcluster_nameи выйдите изpsql:replication_db=# ALTER SYSTEM SET cluster_name='host2';ALTER SYSTEMreplication_db=# \qДля настройки синхронного режима фиксации транзакций реплика должна быть каким-то образом названа.
Для хранения имени реплики используется параметр
cluster_name. -
В сеансе Host2 остановите экземпляр Pangolin с репликой:
[postgres@ServerName ~]$ pg_ctl stop -D /home/postgres/replicawaiting for server to shut down.... doneserver stopped -
В сеансе
psqlна Host1 проверьте текущие значения параметров режима синхронизации:replication_db=# SELECT name, setting FROM pg_settings WHERE name IN('synchronous_commit', 'synchronous_standby_names');name | setting---------------------------+---------synchronous_commit | onsynchronous_standby_names |(2 rows)Вспомним, что при значении
synchronous_commit=onна мастере фиксация транзакции завершается только после попадания соответствующей журнальной записи на накопитель реплики.Однако, этого недостаточно для включения синхронного режима.
Также в параметре
synchronous_standby_namesнеобходимо указать имя реплики, с которой требуется синхронное подтверждение транзакций. -
В сеансе
psqlна Host1 укажите имя реплики в параметреsynchronous_standby_names:replication_db=# ALTER SYSTEM SET synchronous_standby_names = host2;ALTER SYSTEMreplication_db=# SELECT pg_reload_conf();pg_reload_conf----------------t(1 row)Теперь синхронный режим должен работать. Проверим это.
-
В сеансе
psqlна Host1 начните транзакцию, вставьте строку и зафиксируйте транзакцию:replication_db=# BEGIN;BEGINreplication_db=*# INSERT INTO some_table VALUES('Строка, вставленная в синхронном режиме');INSERT 0 1replication_db=*# COMMIT;Команда
COMMITна мастере ожидает подтверждения от реплики, экземпляр которой ранее был остановлен. -
В сеансе Host2 запустите экземпляр Pangolin с репликой:
[postgres@ServerName ~]$ pg_ctl start -D /home/postgres/replica -l logfilewaiting for server to start.... doneserver started -
В сеансе
psqlна Host1 убедитесь, что транзакция завершена:COMMITФиксация транзакции успешно завершена после того как реплика подтвердила, что записала соответствующую журнальную запись на накопитель.
Конфликты при выполнении запросов
-
В сеансе Host2 подключитесь в
psqlк базе данныхreplication_db:[postgres@ServerName ~]$ psql -d replication_db -h localhostpsql (15.5)Type "help" for help. -
В сеансе
psqlна Host2 проверьте значение параметраmax_standby_streaming_delay:replication_db=# SHOW max_standby_streaming_delay;max_standby_streaming_delay-----------------------------30s(1 row)Вспомним, что параметр
max_standby_streaming_delayотвечает за временную задержку применения записей WAL, конфликтующих с выполняемыми на реплике запросами. -
В сеансе
psqlна Host2 начните транзакцию с уровнем изоляцииREPEATABLE READи выполните запрос к таблицеsome_table:replication_db=# BEGIN ISOLATION LEVEL REPEATABLE READ;BEGINreplication_db=*# SELECT * FROM some_table;value------------------------------------------Строка, вставленная на мастереСтрока, вставленная в синхронном режиме(2 rows)Транзакция была начата с уровнем изоляции
REPEATABLE READ, то есть при выполнении первого оператора в ней (SELECT) был создан снимок данных, который будет использован на протяжении всей транзакции. -
В сеансе
psqlна Host1 выполните обновление в таблицеsome_tableи очистку устаревших версий строк:replication_db=# UPDATE some_table SET value = value || ' - обновление';UPDATE 2replication_db=# VACUUM;VACUUM -
В сеансе
psqlна Host2 не позднее чем через 30 секунд после очистки повторите выполнение запроса :replication_db=*# SELECT * FROM some_table;value------------------------------------------Строка, вставленная на мастереСтрока, вставленная в синхронном режиме(2 rows)Запрос на реплике отработал без изменений с ранее построенным снимком данных.
При этом, несмотря на то что версии строк, участвующие в построенном снимке данных, на мастере были очищены, применение результатов очистки на реплике было задержано на 30 секунд.
-
В сеансе
psqlна Host2 по прошествии 30 секунд после очистки повторите выполнение запроса:replication_db=*# SELECT * FROM some_table;FATAL: terminating connection due to conflict with recoveryDETAIL: User query might have needed to see row versions that must be removed.HINT: In a moment you should be able to reconnect to the database and repeat your command.server closed the connection unexpectedlyThis probably means the server terminated abnormallybefore or while processing the request.The connection to the server was lost. Attempting reset: Succeeded.В то время как по прошествии 30 секунд результаты очистки были применены на реплике, и устаревшие для мастера версии строк были удалены и на реплике, хотя они еще были ей нужны.
Это можно исправить путем включения обратной связи реплики с мастером. Тогда реплика будет сообщать мастеру номер самой ранней активной транзакции (
xmin), используемой на реплике, и мастер будет его учитывать при определении горизонта очистки. -
В сеансе
psqlна Host2 включите обратную связь:replication_db=# ALTER SYSTEM SET hot_standby_feedback = on;ALTER SYSTEMreplication_db=# SELECT pg_reload_conf();pg_reload_conf----------------t(1 row) -
В сеансе
psqlна Host2 посмотрите значение параметраwal_receiver_status_interval:replication_db=# SHOW wal_receiver_status_interval;wal_receiver_status_interval------------------------------10s(1 row)Параметр
wal_receiver_status_intervalуказывает, как часто реплика должна сообщать мастеру о своем состоянии.Повторим тот же эксперимент, но с включенной обратной связью.
-
В сеансе
psqlна Host2 начните транзакцию с уровнем изоляцииREPEATABLE READи выполните запрос к таблицеsome_table:replication_db=# BEGIN ISOLATION LEVEL REPEATABLE READ;BEGINreplication_db=*# SELECT * FROM some_table;value-------------------------------------------------------Строка, вставленная на мастере - обновлениеСтрока, вставленная в синхронном режиме - обновление(2 rows) -
В сеансе
psqlна Host1 выполните обновление в таблицеsome_tableи очистку устаревших версий строк:replication_db=# UPDATE some_table SET value = value || ' - еще обновление';UPDATE 2replication_db=# VACUUM VERBOSE some_table;INFO: vacuuming "replication_db.public.some_table"INFO: finished vacuuming "replication_db.public.some_table": index scans: 0pages: 0 removed, 1 remain, 1 scanned (100.00% of total)tuples: 0 removed, 4 remain, 2 are dead but not yet removable, oldest xmin: 800removable cutoff: 800, which was 1 XIDs old when operation endedfrozen: 0 pages from table (0.00% of total) had 0 tuples frozenindex scan not needed: 0 pages from table (0.00% of total) had 0 dead item identifiers removedavg read rate: 0.000 MB/s, avg write rate: 386.757 MB/sbuffer usage: 10 hits, 0 misses, 5 dirtiedWAL usage: 5 records, 5 full page images, 27247 bytessystem usage: CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 sINFO: vacuuming "replication_db.pg_toast.pg_toast_16391"INFO: finished vacuuming "replication_db.pg_toast.pg_toast_16391": index scans: 0pages: 0 removed, 0 remain, 0 scanned (100.00% of total)tuples: 0 removed, 0 remain, 0 are dead but not yet removable, oldest xmin: 800removable cutoff: 800, which was 1 XIDs old when operation endedfrozen: 0 pages from table (100.00% of total) had 0 tuples frozenindex scan not needed: 0 pages from table (100.00% of total) had 0 dead item identifiers removedavg read rate: 0.000 MB/s, avg write rate: 0.000 MB/sbuffer usage: 3 hits, 0 misses, 0 dirtiedWAL usage: 0 records, 0 full page images, 0 bytessystem usage: CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 sVACUUMОчистка не смогла удалить устаревшие для мастера версии строк. Об этом говорит запись:
tuples: 0 removed, 4 remain, 2 are dead but not yet removable, oldest xmin: 800Две строки мертвые, но еще не удалены (
2 are dead but not yet removable).Также указан номер самой ранней активной транзакции (
oldest xmin: 800), удерживающей горизонт очистки. -
В сеансе
psqlна Host1 проверьте состояние слота репликации:replication_db=# SELECT slot_name, active, xmin FROM pg_replication_slots;slot_name | active | xmin--------------+--------+------replica_slot | t | 800(1 row)Тот же
xminхранит слот репликации, то есть именно он удерживает горизонт очистки.Необходимо учитывать, что при использовании слота репликации совместно с режимом обратной связи слот будет задерживать не только очистку сегментов WAL, но и очистку мертвых версий строк, что может привести к разрастанию файлов данных.
-
В сеансе
psqlна Host2 по прошествии 30 секунд после очистки повторите выполнение запроса:replication_db=*# SELECT * FROM some_table;value-------------------------------------------------------Строка, вставленная на мастере - обновлениеСтрока, вставленная в синхронном режиме - обновление(2 rows)Запрос успешно выполнен. Нужные реплике устаревшие версии строк удалены не были.
-
В сеансе
psqlна Host2 зафиксируйте транзакцию:replication_db=*# COMMIT;COMMITПосле завершения транзакции на реплике ее
xminпродвинулся вперед и теперь очистка может быть выполнена.Проверим это.
-
В сеансе
psqlна Host1 повторите очистку устаревших версий строк:replication_db=# VACUUM VERBOSE some_table;INFO: vacuuming "replication_db.public.some_table"INFO: finished vacuuming "replication_db.public.some_table": index scans: 0pages: 0 removed, 1 remain, 1 scanned (100.00% of total)tuples: 2 removed, 2 remain, 0 are dead but not yet removable, oldest xmin: 801removable cutoff: 801, which was 0 XIDs old when operation endednew relfrozenxid: 800, which is 1 XIDs ahead of previous valuefrozen: 0 pages from table (0.00% of total) had 0 tuples frozenindex scan not needed: 0 pages from table (0.00% of total) had 0 dead item identifiers removedavg read rate: 0.000 MB/s, avg write rate: 504.032 MB/sbuffer usage: 9 hits, 0 misses, 6 dirtiedWAL usage: 6 records, 6 full page images, 35292 bytessystem usage: CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 sINFO: vacuuming "replication_db.pg_toast.pg_toast_16391"INFO: finished vacuuming "replication_db.pg_toast.pg_toast_16391": index scans: 0pages: 0 removed, 0 remain, 0 scanned (100.00% of total)tuples: 0 removed, 0 remain, 0 are dead but not yet removable, oldest xmin: 801removable cutoff: 801, which was 0 XIDs old when operation endednew relfrozenxid: 801, which is 1 XIDs ahead of previous valuefrozen: 0 pages from table (100.00% of total) had 0 tuples frozenindex scan not needed: 0 pages from table (100.00% of total) had 0 dead item identifiers removedavg read rate: 0.000 MB/s, avg write rate: 0.000 MB/sbuffer usage: 2 hits, 0 misses, 0 dirtiedWAL usage: 1 records, 0 full page images, 266 bytessystem usage: CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 sVACUUMДействительно, две версии строки успешно удалены, а
xminтеперь равен 801:tuples: 2 removed, 2 remain, 0 are dead but not yet removable, oldest xmin: 801
Переключение на реплику
-
Выйдите из сеанса
psqlна Host1:replication_db=# \q -
В сеансе Host1 узнайте
pidпроцессаpostgresи завершите его принудительно:[postgres@ServerName ~]$ head -n 1 /pgdata/\{major-minor\}.0/data/postmaster.pid15954[postgres@ServerName ~]$ kill -9 15954Таким образом был смоделирован сбой на мастере. Контрольная точка при завершении работы выполнена не была, часть сгенерированных записей WAL до реплики могла не дойти.
В случае сбоя для минимизации времени простоя можно выполнить переключение клиентов на реплику.
Для этого необходимо повысить ее статус до мастера.
-
В сеансе
psqlна Host2 повысьте статус реплики до мастера:replication_db=# SELECT pg_promote();pg_promote------------t(1 row)replication_db=# SELECT pg_is_in_recovery();pg_is_in_recovery-------------------f(1 row)Повышение статуса выполнено с использованием функции
pg_promote.Функция
pg_is_in_recoveryпоказывает, что бывшая реплика теперь не находится в режиме восстановления, то есть является мастером.Теперь восстановим бывший мастер и подключим его к новому мастеру в качестве реплики.
Для синхронизации данных используем утилиту
pg_rewind. -
В сеансе
psqlна Host2 создайте слот репликации:replication_db=# SELECT pg_create_physical_replication_slot('replica_slot');pg_create_physical_replication_slot-------------------------------------(replica_slot,)(1 row) -
В сеансе
psqlна Host2 убедитесь, что включено сохранение полных образов страниц:replication_db=# SHOW full_page_writes;full_page_writes------------------on(1 row)Для работы утилиты
pg_rewindтребуется включение записи полных образов страниц на источнике, которым В этом случае является новый мастер.Помимо этого, целевой экземпляр должен быть либо инициализирован с подсчетом контрольных сумм, либо на нем должно быть установлено значение параметра
wal_log_hits=on, при котором полные образы страниц сохраняются в WAL даже при незначительных изменениях в них, а именно изменениях информационных битов в версиях строк.Ранее мы включили подсчет контрольных сумм страниц на целевом экземпляре.
-
В сеансе
psqlна Host2 создайте пароль для ролиpostgres:replication_db=# ALTER ROLE postgres PASSWORD 'postgres';ALTER ROLEУтилита
pg_rewindдолжна подключаться экземпляру-источнику от имени суперпользователя, поэтому для удаленного подключения потребовалось задать пароль для ролиpostgres. -
В сеансе Host1 выполните синхронизацию с новым мастером с использованием утилиты
pg_rewind, указав в параметре--source-serverIP-адрес хоста Host2:[postgres@ServerName ~]$ pg_rewind -D /pgdata/\{major-minor\}.0/data --source-server 'host={IP-Address} user=postgres password=postgres' -R -Ppg_rewind: connected to serverpg_rewind: servers diverged at WAL location 0/30D9E90 on timeline 1pg_rewind: rewinding from last common checkpoint at 0/30D9DB0 on timeline 1pg_rewind: reading source file listpg_rewind: reading target file listpg_rewind: reading WAL in targetpg_rewind: need to copy 53 MB (total source directory size is 85 MB)54441/54441 kB (100%) copiedpg_rewind: creating backup label and updating control filepg_rewind: syncing target data directorypg_rewind: Done!С использованием ключа
--source-serverуказана строка подключения к экземпляру-источнику (новому мастеру), а с помощью ключа-Dуказан каталог кластера баз данных целевого экземпляра (новой реплики).С использованием ключа
-Rдано указание, что необходимо подготовить целевой кластер для использования в качестве реплики (по аналогии сpg_basebackup -R).При этом файл
postgresql.auto.confкопируется с нового мастера в исходном виде и затем в его конец добавляется параметрprimary_conninfo. Поэтому неперекрытые значения параметров сохраняются для новой реплики, включая значениеprimary_slot_name.Ключ
-Pиспользован для вывода сообщений о прогрессе выполнения синхронизации.В результате
pg_rewindнашел место расхождения журнальных записей двух экземпляров, определил общую для них ближайшую контрольную точку и заменил страницы данных на новой реплике измененными на новом мастере с момента найденной контрольной точки. -
В сеансе Host1 посмотрите содержимое каталога данных:
[postgres@ServerName ~]$ ls /pgdata/\{major-minor\}.0/data/backup_label pg_commit_ts pg_integrity pg_perf_insights pg_replslot pg_stat_tmp PG_VERSION postgresql.confbackup_label.old pg_dynshmem pg_logical pg_pp_cache pg_serial pg_subtrans pg_wal PRODUCT_VERSIONbase pg_hba.conf pg_multixact pg_prep_stats pg_snapshots pg_tblspc pg_xact standby.signalglobal pg_ident.conf pg_notify pg_quota.conf pg_stat pg_twophase postgresql.auto.conf tracingПо аналогии с
pg_basebackup -Rутилитаpg_rewindсоздала файлыstandby.signal,postgresql.auto.confиbackup_label. -
В сеансе Host1 запустите подготовленный экземпляр Pangolin:
[postgres@ServerName ~]$ pg_ctl start -l logfilewaiting for server to start.... doneserver started -
В сеансе Host1 запустите
psqlи проверьте состояние восстановления данных в экземпляре:[postgres@ServerName ~]$ psql -d replication_dbpsql (15.5)Type "help" for help.replication_db=# SELECT pg_is_in_recovery();pg_is_in_recovery-------------------t(1 row)Теперь бывший мастер находится в состоянии восстановления, то есть является новой репликой.
-
В сеансе
psqlна Host2 вставьте новую строку в таблицуsome_table:replication_db=# INSERT INTO some_table VALUES('Строка, вставленная на новом мастере');INSERT 0 1 -
В сеансе
psqlна Host1 сделайте запрос к таблицеsome_table:replication_db=# SELECT * FROM some_table;value------------------------------------------------------------------------Строка, вставленная на новом мастереСтрока, вставленная на мастере - обновление - еще обновлениеСтрока, вставленная в синхронном режиме - обновление - еще обновление(3 rows)Вставленная строка успешно «добралась» с нового мастера на новую реплику.
Экземпляры поменялись ролями.
Завершение
Завершение на виртуальной машине Host1
-
В сеансе
psqlповысьте статус экземпляра до мастера:replication_db=# SELECT pg_promote();pg_promote------------t(1 row) -
В сеансе
psqlудалите слот репликации:replication_db=# SELECT pg_drop_replication_slot('replica_slot');pg_drop_replication_slot--------------------------(1 row) -
В сеансе
psqlподключитесь к базе данныхpostgresи удалите базу данныхreplication_db:replication_db=# \c postgresYou are now connected to database "postgres" as user "postgres".postgres=# DROP DATABASE replication_db;DROP DATABASE -
В сеансе
psqlудалите рольreplicator:postgres=# DROP ROLE replicator;DROP ROLE -
В сеансе
psqlсбросьте значения всех конфигурационных параметров и выйдите изpsql:postgres=# ALTER SYSTEM RESET ALL;ALTER SYSTEMpostgres=# \q -
Перезапустите экземпляр Pangolin:
[postgres@ServerName ~]$ pg_ctl restart -l logfile
waiting for server to shut down.... done
server stopped
waiting for server to start.... done
server started
-
Выйдите из оболочки, запущенной от имени пользователя
postgres:[postgres@ServerName ~]$ exitlogout
Завершение на виртуальной машине Host2
-
В сеансе
psqlвыйдите из psql:replication_db=# \q -
Остановите экземпляр Pangolin с каталогом данных
replica:[postgres@ServerName ~]$ pg_ctl stop -D /home/postgres/replicawaiting for server to shut down.... doneserver stopped -
Удалите каталог данных
replica:[postgres@ServerName ~]$ rm -rf ~/replica -
Запустите исходный экземпляр Pangolin:
[postgres@ServerName ~]$ pg_ctl start -l logfilewaiting for server to start.... doneserver started -
Выйдите из оболочки, запущенной от имени пользователя
postgres:[postgres@ServerName ~]$ exitlogout
Самопроверка
Вопрос 1
В сеансе psql выполнена следующая последовательность команд:
postgres=# SHOW hot_standby;
hot_standby
-------------
on
(1 row)
postgres=# SHOW hot_standby_feedback;
hot_standby_feedback
----------------------
off
(1 row)
postgres=# SELECT pg_is_in_recovery();
pg_is_in_recovery
-------------------
t
(1 row)
Какие из перечисленных SQL-команд могут быть выполнены в этом сеансе psql? Выберите все правильные варианты
Вопрос 2
Что из перечисленного дополнительно выполняет утилита pg_basebackup в случае использования ключа -R при создании базовой резервной копии? Выберите все правильные варианты
Вопрос 3
Настроена потоковая репликация между мастером и двумя репликами. На одной из реплик произошел сбой, в результате которого экземпляр Pangolin был остановлен.
После этого в сеансе psql на мастере выполнена следующая последовательность команд:
postgres=# BEGIN ISOLATION LEVEL REPEATABLE READ;
BEGIN
postgres=# SELECT name, setting FROM pg_settings WHERE name IN('synchronous_commit', 'synchronous_standby_names');
name | setting
---------------------------+---------
synchronous_commit | on
synchronous_standby_names |
(2 rows)
COMMIT;
Что произойдет в результате вызова последней команды?
Вопрос 4
Между мастером и репликой настроена потоковая репликация с использованием слота репликации с именем replica_slot.
В сеансе psql на мастере выполнена следующая команда:
some_db=# SELECT slot_name, active, xmin, restart_lsn, wal_status FROM pg_replication_slots;
slot_name | active | xmin | restart_lsn | wal_status
--------------+--------+------+-------------+------------
replica_slot | t | 820 | 0/9000198 | reserved
(1 row)
Может ли мастер очистить устаревшие для него версии строк, необходимые для выполнения запросов на реплике?