Перейти к основному содержимому

Лекция 13. Репликация

В этой главе:

  • Общие задачи репликации
  • Способы репликации
  • Физическая репликация
  • Логическая репликация.

Общие задачи репликации

Высокая доступность (HA - High Availability):

  • снижение вероятности отказа
  • повышение времени наработки на отказ
  • снижение времени недоступности сервиса
  • снижение суммарного времени простоя системы за период
  • повышение ремонтопригодности и удобства в обслуживании

Высокая производительность (HP - High Performance):

  • увеличение количества обрабатываемых транзакций
  • снижение времени латентности
  • увеличение объема обрабатываемой информации
  • увеличение допустимого количества одновременно обслуживаемых клиентов

Репликация данных заключается в непрерывной передаче данных с учетом их изменений между серверами баз данных с целью их синхронизации на разных серверах. Репликация лежит в основе построения вычислительных кластеров двух основных разновидностей:

  • высокодоступные-HA(HighAvailability);
  • высокопроизводительнные-HP(HighPerformance).

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

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

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

PostgreSQL предоставляет возможности репликации данных несколькими способами.

Способы репликации

Физическая репликация:

  • передача записей WAL с помощью потоковой репликации или копированием файлов сегментов WAL;
  • передача записей WAL в одну сторону с мастера на реплику;
  • требуется бинарная совместимость мастера и реплики;
  • на реплике могут быть разрешены только читающие запросы;
  • реплицируются все базы данных экземпляра, нельзя выбрать отдельный объект.

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

  • передача декодированной информации о командах INSERT, UPDATE, DELETE и TRUNCATE по протоколу репликации;
  • нет выделенной роли сервера, есть публикация и подписка;
  • требуется совместимость на уровне протокола, бинарная совместимость не требуется;
  • реплицируются конкретные объекты.

В PostgreSQL реализованы две разновидности репликации:

  • физическая;
  • логическая.

Физическая репликация построена на передаче записей WAL с мастер сервера на реплику. Мастер выполняет пишущие и читающие транзакции, формируя записи WAL. Эти записи передаются на сервер- реплику и применяются так же, как это происходит при восстановлении сервера после сбоя: процесс startup выполняет записи WAL. Однако, при восстановлении после сбоя процесс startup завершается после выполнения всех записей WAL в имеющихся сегментах. А на реплике процесс startup работает непрерывно. Такое непрерывное восстановление данных на реплике, поступающих с мастера, постоянно обновляет данные в базах реплики. Реплицируются все базы кластера, выделить отдельный объект невозможно. Поток данных при физической репликации направлен в одну сторону от мастера к реплике, причем мажорные версии PostgreSQL должны быть одинаковыми, как и сама платформа, так как для применения потока репликации должна обеспечиваться бинарная совместимость.

Логическая репликация гибче - нет выделенных ролей, есть публикации и подписки, а поток данных может быть двунаправленным. Реплицируются команды INSERT, UPDATE, DELETE и TRUNCATE, применительно к конкретным таблицам. В логической репликации не требуется бинарная совместимость - мажорные версии могут быть разными.

Физическая репликация

  • Реплика изначально - физическая копия мастера.
  • Файл standby.signal вынуждает сервер при старте войти в режим непрерывного восстановления.
  • На реплике должна быть настроена строка подключения к мастеру.
  • В режиме восстановления реплика получает поток репликации с мастера, постоянно обновляя данные в своих базах.

Сервер репликации изначально создается как физическая копия мастера. Сделать ее можно, например, утилитой pg_basebackup. Для того, чтобы сервер-реплика не завершил процесс startup после прочтения имеющихся сегментов журнала транзакций, а вместо этого перешел в режим постоянного восстановления в каталоге PGDATA должен быть создан пустой файл standby.signal. Также реплика должна быть настроена на подключение к мастеру по протоколу репликации, что достигается с помощью строки подключения, заданной параметром primary_conninfo.

Мастер должен разрешать подключение к нему по протоколу репликации. Это делается настройкой в pg_hba.conf. Уровень журнала WAL, задаваемый параметром wal_level должен быть установлен в значение replica (это настройка по умолчанию).

После запуска реплики в списке процессов должен присутствовать процесс startup, выполняющий непрерывное восстановление. В потоковом режиме репликации на мастере должен запуститься процесс walsender, отправляющий по сетевому соединению записи WAL процессу walreceiver, запускающимся на реплике. https://www.postgresql.org/docs/15/warm-standby.html#STREAMING-REPLICATION.

Проверка настроек мастера

[postgres@p620 ~]$ psql -c '\dconfig (max_wal_se|wal_lev)*' 
List of configuration parameters
Parameter | Value
-----------------+---------
max_wal_senders | 10
wal_level | replica
(2 rows)

[postgres@p620 ~]$ psql -c "SELECT type,user_name,address,auth_method FROM pg_hba_file_rules() WHERE 'replication'=ANY(database)"
type | user_name | address | auth_method
-------+-----------+-----------+---------------
local | {all} | | peer
host | {all} | 127.0.0.1 | scram-sha-256
host | {all} | ::1 | scram-sha-256
(3 rows)

Прежде, чем приступать к созданию реплики, следует проверить настройки на мастере:

  • max_wal_senders - ограничение на максимальное количество процессов wal sender, которые передают поток репликации;
  • wal_level - уровень подробности записей в журнале предзаписи, для физической репликации требуется replica - уровень по умолчанию.

Создание физической копии и физическая репликация требуют разрешения на мастере в файле pg_hba.conf. В этом файле должны быть соответствующие разрешения, где в поле имени БД указано replica. В примере на слайде при подключении реплики через локальный Unix-сокет аутентификация будет выполняться средставми ОС: роль в базе данных должна называться так же, как текущий пользователь в ОС. Пароль при этом не потребуется. При подключении через сетевой интерфейс, соответствующий имени узла localhost, потребуется аутентификация по паролю средствами СУБД. https://www.postgresql.org/docs/15/runtime-config-replication.html#RUNTIME-CONFIG-REPLICATION-PRIMARY.

Подготовка реплики

[student@p620 ~]$ sudo -iu postgres
[postgres@p620 ~]$ pwd
/var/lib/postgres
[postgres@p620 ~]$ mkdir rpl_pgdata
[postgres@p620 ~]$ pg_basebackup -D rpl_pgdata/ -c fast -R
[postgres@p620 ~]$ ls rpl_pgdata/
backup_label pg_hba.conf.orig pg_quota.conf PG_VERSION
backup_manifest pg_ident.conf pg_replslot pg_wal
base pg_integrity pg_serial pg_xact
current_logfiles pg_logical pg_snapshots postgresql.auto.conf
global pg_multixact pg_stat postgresql.conf
pg_commit_ts pg_notify pg_stat_tmp postgresql.conf.bak
pg_dynshmem pg_perf_insights pg_subtrans PRODUCT_VERSION
pg_hba.conf pg_pp_cache pg_tblspc standby.signal
pg_hba.conf.bak pg_prep_stats pg_twophase tracing

В примере на слайде создан каталог PGDATA для реплики, в который выполнено физическое копирование мастер сервера. При этом была использована опция -R, в ранних версиях PostgreSQL создававшая файл настроек восстановления recovery.conf. Начиная с 12-й версии PostgreSQL этот файл конфигурации не используется, а настройки из него перенесены в основную конфигурацию. В примере эта опция нужна для:

  • создания файла в каталоге PGDATA реплики пустого файла standby.signal - он необходим для выполнения непрерывного восстановления процессом startup;
  • внесения параметра primary_conninfo, устанавливающего строку соединения реплики с мастером в файл postgresql.auto.conf.

На мастер сервере мог быть заранее создан слот репликации (в примере выше это не сделано) для гарантии того, что реплика получит все необходимые сегменты журнала предзаписи WAL. В таком случае, к команде pg_basebackup следовало бы добавить опцию -S, после которой было бы указано имя слота. В результате этого в файле postgresql.auto.conf появилась бы настройка primary_slot_name, так как эта работа была бы выполнена также опцией -R программы pg_basebackup.

https://www.postgresql.org/docs/15/runtime-config-replication.html#RUNTIME-CONFIG-REPLICATION-STANDBY.

https://www.postgresql.org/docs/15/app-pgbasebackup.html.

Настройка реплики

[postgres@p620 ~]$ chmod -R go= rpl_pgdata/; cd rpl_pgdata/ 
[postgres@p620 rpl_pgdata]$ echo 'port = 6432' >> postgresql.auto.conf
[postgres@p620 rpl_pgdata]$ sed -i '/#log/d' postgresql.auto.conf
[postgres@p620 rpl_pgdata]$ cat postgresql.auto.conf
# Do not edit this file manually!
# It will be overwritten by the ALTER SYSTEM command.
primary_conninfo = 'user=postgres passfile=''/var/lib/postgres/.pgpass'' channel_binding=prefer port=5432 client_encoding=UTF8 sslmode=prefer sslcompression=0 sslsni=1 ssl_min_protocol_version=TLSv1.2 gssencmode=prefer krbsrvname=postgres target_session_attrs=any'
port = 6432
  • Реплика по умолчанию прослушивает порт TCP 5432, как и мастер.
  • Записав настройку параметра port в postgresql.auto.conf, заставим реплику прослушивать порт 6432.
  • Также выключим сборщик журналов отчета.

Так как каталог данных реплики получен вызовом pg_basebackup, он содержит копию содержимого PGDATA мастера. В том числе там находятся и копии его файлов конфигурации. Если бы реплика запускалась на другом компьютере с такой же платформой, одинаковой мажорной версией и таким же размещением PGDATA в файловой системе реплики, сервер реплики можно было бы уже запустить.

В примере, однако, сервер реплики запускается в той же виртуальной машине, что и мастер. Следовательно, некоторые настройки следует изменить. В нашем случае следует заменить номер TCP порта, который будет прослушивать реплика, а также изменить настройки журналов отчетов. Проще всего это сделать, изменив настройки в postgresql.conf. Для наглядности, сделаем это в файле postgresql.auto.conf, который обычно не следует исправлять, так как он содержит в себе изменения, сделанные с помощью ALTER SYSTEM.

В файл postgresql.auto.conf включим настройки порта TCP 6432, который будет прослушивать реплика, а также удалим настройки для сборщика журналов отчета.

Для запуска сервера необходимо чтобы права на каталог PGDATA были либо 700 (rdwx------), либо 750 (drwxr-x---). Права были установлены рекурсивно командой chmod -R. Конструкция go= в этой команде обозначает отсутствие всех прав у группы владельцев и всех остальных.

Запуск реплики

[postgres@p620 ~]$ pg_ctl start -D ~/rpl_pgdata/ -l ~/rpl.log 
ожидание запуска сервера.... готово
сервер запущен
[postgres@p620 ~]$ tail -14 ~/rpl.log
2024-11-03 10:15:21.190 MSK [19263] LOG: listening on IPv4 address "0.0.0.0", port 6432
2024-11-03 10:15:21.190 MSK [19263] LOG: listening on IPv6 address "::", port 6432
2024-11-03 10:15:21.196 MSK [19263] LOG: listening on Unix socket "/tmp/.s.PGSQL.6432"
2024-11-03 10:15:21.207 MSK [19267] LOG: database system was shut down in recovery at 2024-11-03 10:13:2 2024-11-03 10:15:21.208 MSK [19268] LOG: idle terminator started
2024-11-03 10:15:21.208 MSK [19267] LOG: entering standby mode
2024-11-03 10:15:21.233 MSK [19267] LOG: redo starts at 0/C000070
2024-11-03 10:15:21.233 MSK [19267] LOG: consistent recovery state reached at 0/C000150
2024-11-03 10:15:21.233 MSK [19267] LOG: invalid record length at 0/C000198: wanted 26, got 0 2024-11-03 10:15:21.233 MSK [19263] LOG: database system is ready to accept read-only connections 2024-11-03 10:15:21.238 MSK [19263] LOG: Start integrity check launcher
2024-11-03 10:15:21.239 MSK [19270] LOG: Start of integrity check
2024-11-03 10:15:21.239 MSK [19271] LOG: License checker started
2024-11-03 10:15:21.248 MSK [19269] LOG: started streaming WAL from primary at 0/C000000 on timeline 1

Мастер запущен средствами systemctl, а реплику запустим вручную, воспользовавшись pg_ctl, где опцией -D указан каталог PGDATA реплики. Вывод в журнал отчета свидетельствует о том, что реплика успешно запустилась и прослушивает порт TCP 6432. Также в отчете видно, что реплика находится в режиме восстановления, непрерывно получая поток WAL. Убедиться, что мастер и реплика работают можно так:

$ ps f -C postgres
PID TTY STAT TIME COMMAND
19263 ? Ss 0:00 /usr/pangolin-6.2.0/bin/postgres -D /var/lib/postgres/rpl_pgdata
19265 ? Ss 0:00 \_ postgres: checkpointer
19266 ? Ss 0:00 \_ postgres: background writer
19267 ? Ss 0:00 \_ postgres: startup recovering 00000001000000000000000C
19268 ? Ss 0:00 \_ postgres: idle sessions terminator
19269 ? Ss 0:00 \_ postgres: walreceiver
19270 ? Ss 0:00 \_ postgres: integrity check launcher
19271 ? Ss 0:00 \_ postgres: license checker
810 ? Ss 0:19 /usr/pangolin-6.2.0/bin/postgres -D /pgdata/06/data
905 ? Ss 0:00 \_ postgres: logger
909 ? Ss 0:00 \_ postgres: checkpointer
910 ? Ss 0:00 \_ postgres: background writer
912 ? Ss 0:00 \_ postgres: idle sessions terminator
922 ? Ss 0:00 \_ postgres: walwriter
923 ? Ss 0:00 \_ postgres: license checker
924 ? Ss 0:00 \_ postgres: autovacuum launcher
925 ? Ss 0:00 \_ postgres: autounite launcher
926 ? Ss 0:00 \_ postgres: integrity check launcher
927 ? Ss 0:00 \_ postgres: logical replication launcher
19272 ? Ss 0:00 \_ postgres: walsender postgres [local] streaming 0/C000198

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

На мастере и реплики запускаются различные наборы процессов, отличающие мастер и реплику друг от друга.

Мастер:

  • walwriter - процесс записи в журнал предзаписи WAL
  • autovacuum - процесс очистки
  • walsender - процесс передачи потока WAL на реплику

Реплика:

  • startup - выполняет непрерывное восстановление записей WAL
  • walreceiver - процесс получения записей WAL из потока

Так как мастер и реплика функционируют по-разному, то и наборы процессов экземпляров у них отличаются.

На мастере:

  • процесс walwriter помещает записи WAL из кеша WAL в файлы сегментов журнала предзаписи, этот процесс не может работать на реплике, так как реплика получает эти записи из потока;
  • автоочистка работает только на мастере;
  • процесс walsender, посылающий поток репликации на реплику, работает на мастере, но при каскадной репликации может работать и на реплике.

На реплике:

  • процесс wal receiver получает записи WAL из потока репликации;
  • процесс startup выполняет непрерывное восстановление с помощью полученных из потока записей WAL.

Состояние мастера

[postgres@p620 ~]$ psql -q
postgres=# SELECT pg_is_in_recovery();
pg_is_in_recovery
-------------------
f
(1 строка)
postgres=# SELECT pid, backend_type FROM pg_stat_activity;
pid | backend_type
------+------------------------------
924 | autovacuum launcher
925 | autounite launcher
926 | integrity check launcher
927 | logical replication launcher
19272 | walsender
19762 | client backend
910 | background writer
909 | checkpointer
922 | walwriter
(9 строк)

Функция pg_is_in_recovery() выводит false, если сервер не находится в состоянии восстановления.

Мастер не находится в состоянии восстановления и может выполнять пишущие запросы.

Простой способ определить, в каком состоянии находится сервер - восстанавливается или же сервер готов принимать любые запросы, в том числе и на запись, позволяет функция pg_is_in_recovery(). На мастере эта функция возвращает false. https://www.postgresql.org/docs/15/functions-admin.html.

В выводе pg_stat_activity видно, что на мастере запущен процессы автоочистки и записи WAL, чего не может быть на реплике. Процесс walsender, запускающийся на мастере для передачи в поток WAL, может быть запущен и на реплике, но лишь в топологии каскадной репликации, когда реплика получает WAL с другой реплики.

Состояние реплики

[postgres@p620 ~]$ psql -q -p 6432 postgres=# SELECT pg_is_in_recovery();
pg_is_in_recovery
-------------------
t
(1 строка)
postgres=# SELECT pid, backend_type FROM pg_stat_activity;
pid | backend_type
------+--------------------------
19270 | integrity check launcher
19881 | client backend
19267 | startup
19266 | background writer
19265 | checkpointer
19269 | walreceiver
(6 строк)
postgres=# CREATE TABLE t();
ERROR: cannot execute CREATE TABLE in a read-only transaction

На реплике функция pg_is_in_recovery() выводит true, поскольку реплика находится в состоянии постоянного восстановления.

Реплика не может выполнять пишущие запросы.

На реплике функция pg_is_in_recovery() возвращает true. https://www.postgresql.org/docs/15/functions-admin.html.

В выводе pg_stat_activity видно, что на реплике запущен процесс получения записей WAL из потока walreceiver, а процесс startup выполняет непрерывное восстановление.

Если включен параметр hot_standby, на реплике разрешаются читающие запросы:

postgres=# \dconfig+ hot_standby
Список параметров конфигурации
Параметр | Значение | Тип | Контекст | Права доступа
-------------+----------+------+------------+---------------
hot_standby | on | bool | postmaster |
(1 строка)

https://www.postgresql.org/docs/15/runtime-config-replication.html#GUC-HOT-STANDBY.

https://www.postgresql.org/docs/15/hot-standby.html.

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

postgres=# \c - - - 5432
postgres=# CREATE DATABASE repbase;
postgres=# \c repbase
repbase=# CREATE TABLE nt (dt timestamp DEFAULT now(), msg text);
repbase=# INSERT INTO nt(msg) VALUES ('Проверка связи...');
repbase=# \c - - - 6432
repbase=# \conninfo
You are now connected to database "repbase" as user "postgres" via socket in "/tmp" at port "6432".
repbase=# SELECT * FROM nt;
dt | msg
----------------------------+-------------------
2024-11-26 06:49:41.851134 | Проверка связи...

В примере выше сначала выполнено подключение к мастеру на порт 5432. В этом сеансе была создана база данных repbase, а в ней была создана таблица nt.

После открытия нового сеанса теперь уже с репликой, к которой подключились на порт 6432, можно убедиться, что база данных есть на реплике, а в базе есть таблица nt с данными. Информация о требуемых действиях на реплике была получена репликой по протоколу репликации с мастера. Никакой интерпретации или планирования запросов реплика не выполняла, процесс startup просто выполнил все действия, выполненные мастером, по записям в потоке WAL.

Состояние репликации на мастере

repbase=# \c - - - 5432
repbase=# SELECT * FROM pg_stat_replication \gx
-[ RECORD 1 ]----+------------------------------
pid | 2971
usesysid | 10
usename | postgres
application_name | walreceiver
client_addr |
client_hostname |
client_port | -1
backend_start | 2024-11-26 06:51:42.717722+03
backend_xmin |
state | streaming
sent_lsn | 0/D6B0FE8
write_lsn | 0/D6B0FE8
flush_lsn | 0/D6B0FE8
replay_lsn | 0/D6B0FE8
write_lag |
flush_lag |
replay_lag |
sync_priority | 0
sync_state | async
reply_time | 2024-11-26 06:51:42.717722+03

Представление pg_stat_replication выдает отчет о текущем статусе репликации.

Некоторые поля представления:

  • state-типрепликации;
  • sync_state-синхроннаяилиасинхроннаярепликация.

Изменение роли реплики

При отказе мастера или его намеренном останове, реплика может взять на себя его функциональность, выполнив повышение роли и став новым мастером.

При повышении роли реплики:

  • применяются все полученные, но еще не примененные, записи WAL;
  • завершает работу процесс startup;
  • разрывается поток репликации, останавливается процесс wal receiver;
  • запускаются все процессы, которые должны работать на мастере;
  • сервер переводится в обычный режим работы и может обслуживать читающие и пишущие транзакции.

Повышение роли выполняется с помощью:

  • функции pg_promote()
  • команды pg_ctl promote
  • триггерного файла, имя которого задает параметр конфигурации promote_trigger_file.

Реплика не может выполнять всю работу, характерную для мастер- сервера. Например, на реплике нельзя выполнять транзакции, изменяющие данные.

В случае выхода из строя мастер-сервера или же его намеренной остановки, например, для обслуживания, реплика может взять на себя всю его функциональность. Для этого на реплике необходимо выполнить операцию, называемую повышением роли (Promote). В результате такого повышения на реплике завершается процесс получения записей WAL, применяются все оставшиеся полученные до этого записи WAL, процесс непрерывного восстановления завершается и запускаются все необходимые на мастере процессы. Сам сервер переводится в обычный режим работы с обслуживанием как читающих, так и пишущих транзакций.

Для повышения роли в PostgreSQL предусмотрено три способа:

  • вызов функции pg_promote()
  • выполнение pg_ctlpromote
  • создание триггерного файла, имя которого задает параметр конфигурации promote_trigger_file.

Необходимо особо отметить, что если старый мастер-сервер не остановлен, то при повышении роли реплики до нового мастера эти два сервера могут продолжать выполнять пишущие транзакции. В таком случае данные будут рассинхронизированы и простого способа восстановления их согласованности не существует. https://www.postgresql.org/docs/15/warm-standby-failover.html.

Повышение роли реплики

postgres=# \c repbase - - 6432
You are now connected to database "repbase" as user "postgres" via socket in "/tmp" at port "6432".
repbase=# SELECT pg_is_in_recovery();
pg_is_in_recovery
-------------------
t
(1 row)
repbase=# SELECT pg_promote();
pg_promote
------------
t
(1 row)
repbase=# INSERT INTO nt(msg) VALUES ('Теперь у нас два мастера.');
INSERT 0 1
repbase=# SELECT * FROM nt;
dt | msg
---------------------------+---------------------------
2024-11-26 06:49:41.851134 | Проверка связи...
2024-11-26 06:54:50.550615 | Теперь у нас два мастера. (2 rows)

В примере на слайде приведен процесс повышения роли реплики до мастер сервера. После этого повышения на бывшей реплике появилась возможность выполнить пишущий запрос, добавляющий данные. Состояние процессов на новом мастере:

repbase=# SELECT pid, backend_type FROM pg_stat_activity;
pid | backend_type
-------+------------------------------
24979 | autovacuum launcher
19270 | integrity check launcher
24971 | client backend
24981 | logical replication launcher
24980 | autounite launcher
19266 | background writer
19265 | checkpointer
24978 | walwriter
(8 строк)

Заметно, что запустились процессы:

  • автоочистки autovacuum launcher;
  • записи WAL из кеша в сегменты журнала WAL walwriter.

Остановлены процессы:

Файловая репликация

Файловая репликация реализуется с помощью организации архива сегментов журнала предзаписи WAL. При этом можно файловую репликацию использовать в качестве дополнения к потоковой, либо же без нее. Если между мастером и репликой используется файловая репликация без поточной, то файлы сегментов журнала попадают в архив либо после их полного заполнения, либо при вызове функции pg_switch_wal(). Это вызывает значительную задержку применения записей WAL на реплике. Если длительная задержка не допускается, то файловый архив сегментов журнала WAL используют в качестве дополнения к потоковой репликации, повышающие ее надежность и позволяющего выполнять восстановление к моменту времени в прошлом (Point In Time Recovery - PITR). Реплика автоматически будет извлекать из архива нужные ей сегменты WAL, командой, заданной параметром restore_command.

Помещать сегменты WAL в архив можно двумя способами:

  • переводом мастер-сервера в режим архивирования, включив параметр archive_mode и настроив команду архивирования параметром archive_command, которая и будет размещать сегменты WAL в архиве после их ротации;
  • с помощью отдельной программы подключающейся к мастеру по протоколу репликации для получения потока WAL и самостоятельного размещения сегментов WAL в архиве.

https://www.postgresql.org/docs/15/runtime-config-wal.html#RUNTIME-CONFIG-WAL-ARCHIVING.

https://www.postgresql.org/docs/15/high-availability.html.

Возможности реплики

Разрешено:

  • читающие запросы: SELECT, COPY TO
  • настройка параметров: SET и RESET
  • читающие транзакции: BEGIN, COMMIT, ROLLBACK
  • резервное копирование

Запрещено:

  • пишущие запросы: INSERT, UPDATE, DELETE,...
  • команды DDL: CREATE, DROP, ALTER,...
  • обслуживание: VACUUM, ANALYZE, REINDEX,...
  • управление доступом

Будет ли реплика вообще отвечать на запросы зависит от параметра hot_standby, который по умолчанию включен. На реплике разрешены читающие запросы и читающие транзакции. Также реплика подходит для выполнения резервного копирования, как логического, так и физического. Можно также устанавливать и сбрасывать сессионные параметры. Запрещены на реплике любые команды и транзакции, изменяющие данные. Запрещены очистка, реиндексирование, сбор данных статистики и подобные операции обслуживания. Нельзя выполнять команды языка DDL, создающие объекты, удаляющие и изменяющие их. Также нельзя выполнять команды управления доступом GRANT и REVOKE. https://www.postgresql.org/docs/15/hot-standby.html.

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

Особенности логической репликации

  • Нет выделенной роли сервера, реплицируются выполненные команды INSERT, UPDATE,DELETE и TRUNCATE в таблицах.
  • Публикация - объект БД, сообщающих о выполненных командах DDL в одной или нескольких таблицах.
  • Подписка - объект БД, собирающий и применяющий сообщения о командах DML, опубликованные одной или несколькими публикациями.

Логическая репликация строится на передаче информации о выполненных в конкретной таблице команд INSERT, UPDATE, DELETE и TRUNCATE. В логической репликации не используются понятия "мастер" и "реплика", вместо этого имеются публикации (Publication) и подписки (Subscription). На одном сервере могут быть одновременно и публикации и подписки, поэтому в отличие от физической репликации, в логической потоки данных разнонаправленные. И подписка и публикация представляют собой специализированные объекты базы данных. Публикация создается для одной или нескольких таблиц, причем можно указать одну таблицу, все таблицы базы данных или заданной схемы, а также указать список публикуемых таблиц. Можно даже указать не все столбцы таблицы, а выбрать требуемые для репликации. Публикуемые строки также можно фильтровать. Подписка - другая сторона логической репликации. При создании подписки указывают строку подключения и имена публикаций (минимум одну), на которые эта подписка оформляется. Поток логической репликации использует обычный протокол репликации, изначально спроектированный для физической репликации. Для этого в логической репликации используется декодирование: публикация извлекает из записей WAL те, которые относятся к указанным выше командам DML, записывающим в публикуемую таблицу. Эти декодированные данные передаются подписке. https://www.postgresql.org/docs/15/logical-replication.html.

Организация логической репликации

  • Процесс wal sender получает декодированные изменения, прочитанные из записей WAL, и отправляет их с помощью сетевого протокола репликации подписчику.
  • На стороне подписчика процесс logical replication worker получает декодированные команды и применяет их.
  • Требуется уровень журнала WAL wal_level=logical.

Декодированные изменения, которые публикатор прочел из журнала WAL, отправляются процессом wal sender в поток логической репликации. Процесс wal sender поддерживает передачу посредством сетевого протокола репликации точно так же, как и при физической репликации. Однако, при логической репликации по протоколу передаются совсем другие сообщения - декодированные команды DML. Поэтому на сервере, где будет работать публикация параметр wal_level должен быть установлен в значение logical. На другой стороне при создании подписки процесс logical replication launcher сообщает postmaster о необходимости породить специальный процесс logical replication worker, который будет обслуживать подписку, получая декодированные сообщения по протоколу репликации. Подписка применяет эти сообщения к таблицам. https://www.postgresql.org/docs/15/logical-replication-publication.html.

https://www.postgresql.org/docs/15/logical-replication-subscription.html.

Настройка wal_level

repbase=# \c - - - 5432
You are now connected to database "repbase" as user "postgres" via socket in "/tmp" at
port "5432".
repbase=# ALTER SYSTEM SET wal_level TO logical ;
ALTER SYSTEM
repbase=# \dconfig+ wal_level
List of configuration parameters
Parameter | Value | Type | Context | Access privileges
-----------+---------+------+------------+-------------------
wal_level | replica | enum | postmaster |
(1 row)
repbase=# \q
[postgres@p620 ~]$ exit
[student@p620 ~]$ sudo systemctl restart postgresql
[student@p620 ~]$ sudo -iu postgres
[postgres@p620 ~]$ psql -d repbase -q
repbase=#

Перед настройкой логической репликации необходимо изменить значение параметра wal_level, определяющий подробность записей в журнале WAL. Для логической репликации должно быть установлено значение logical. Изменение этого параметра требует рестарт экземпляра - контекст postmaster. Поэтому в примере на слайде после установки значения параметра в postgresql.auto.conf посредством ALTER SYSTEM, был осуществлен выход из сеанса psql. Команда ОС exit завершила работу оболочки Bash, запущенной от имени пользователя ОС postgres, вернувшись в сеанс пользователя ОС student. Учетная запись student настроена так, что имеет право выполнять команды с помощью sudo (postgres такого права не имеет и не должен его иметь в соответствии с требованиям безопасности). Пользователь student, используя sudo перезагрузил экземпляр PostgreSQL. Далее снова была запущена оболочка от имени postgres, в которой было выполнено подключение к БД repbase экземпляра сервера, прослушивающего порт 5432. https://www.postgresql.org/docs/15/runtime-config-wal.html#RUNTIME-CONFIG-WAL-SETTINGS.

Создание публикации

repbase=# CREATE TABLE info2pub(id integer PRIMARY KEY, msg text);
CREATE TABLE
repbase=# INSERT INTO info2pub VALUES(1,'Это таблица'),(2,'для логической'),(3,'репликации'),(4,'.'); INSERT 0 4
repbase=# CREATE PUBLICATION pub_info FOR TABLE info2pub;
repbase=# \dRp
List of publications
Name | Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root
----------+----------+------------+---------+---------+---------+-----------+----------
pub_info | postgres | f | t | t | t | t | f
(1 row)
  • Создать публикацию можно командой CREATE PUBLICATION, в публикации можно фильтровать строки по условию и выбирать требуемые столбцы.
  • Параметром publish публикации можно определять команды DML для репликации.
  • В одной публикации можно указывать несколько таблиц, все таблицы БД или схемы.
  • Узнать информацию о публикации в psql можно метакомандой \dRp

В примере выше создана публикация для одной таблицы без фильтрации строк и ограничений по столбцам. Публиковаться будут все команды: INSERT, UPDATE, DELETE и TRUNCATE. https://www.postgresql.org/docs/15/sql-createpublication.html.

Копирование структуры таблицы

[postgres@p620 ~]$ pg_dump -d repbase -t info2pub --schema-only | psql -d repbase -p 6432
...
[postgres@p620 ~]$ psql -p 6432 -d repbase
psql (15.5)
Type "help" for help.
repbase=# \d info2pub
Table "public.info2pub"
Column | Type | Collation | Nullable | Default --------+---------+-----------+----------+---------
id | integer | | not null |
msg | text | | |
Indexes:
"info2pub_pkey" PRIMARY KEY, btree (id)

В примере на слайде средством логического резервного копирования pg_dump скопирована только лишь структура (то есть, команды DDL для воссоздания объектов) таблицы info2pub из базы данных repbase сервера, прослушивающего порт по умолчанию 5432. Эта структура немедленно была использована для создания таблицы с таким же названием в базе repbase на другом сервере. Он прослушивает в примере порт 6432. Пока в таблице info2pub на этом сервере никакой информации нет, есть лишь сама таблица с такими же столбцами, как публикуемая таблица с сервера 5432. https://www.postgresql.org/docs/15/app-pgdump.html.

Создание подписки

repbase=# CREATE SUBSCRIPTION sub_info
CONNECTION 'dbname=repbase'
PUBLICATION pub_info;
NOTICE: created replication slot "sub_info" on publisher CREATE SUBSCRIPTION
Expanded display is on.
List of subscriptions
-[ RECORD 1 ]--------+---------------
Name | sub_info
Owner | postgres
Enabled |t
Publication | {pub_info}
Binary |f
Streaming |f
Two-phase commit |d
Disable on error |f
Synchronous commit | off
Conninfo | dbname=repbase
Skip LSN | 0/0
  • Команда CREATE SUBSCRIPTION создает подписку на публикации.
  • Одна подписка может получать несколько публикаций.
  • Метакоманда \dRs выводит сведения о состоянии подписки.

В примере на слайде создана публикация для одной таблицы без фильтрации строк и ограничений по столбцам. Публиковаться будут все команды: INSERT, UPDATE, DELETE и TRUNCATE. https://www.postgresql.org/docs/15/sql-createpublication.html.

Начальная синхронизация

repbase=# \conninfo
You are connected to database
"repbase" as user "postgres" via
socket in "/tmp" at port "6432".
repbase=# SELECT * FROM info2pub ;
id | msg
----+----------------
1 | Это таблица
2 | для логической
3 | репликации
4 | .
(4 строки)

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

Отменить это поведение можно указав параметр copy_data = false при создании подписки.

При создании публикации по умолчанию происходит автоматическое заполнение таблиц на стороне подписки данными публикатора. Отменить это поведение можно установкой параметра copy_data=false команды CREATE SUBSCRIPION. https://www.postgresql.org/docs/15/sql-createsubscription.html.

В примере выше показано, что после запуска подписки таблица на стороне подписчика была автоматически заполнена данными.

Проверка работы репликации

repbase=# \c - - - 5432
You are now connected to database "repbase" as user "postgres" via socket in "/tmp" at port "5432".
repbase=# INSERT INTO info2pub VALUES (5,'Проверка репликации.');
INSERT 0 1
repbase=# \c - - - 6432
You are now connected to database "repbase" as user "postgres" via socket in "/tmp" at port "6432".
repbase=# SELECT * FROM info2pub ;
id | msg
----+----------------------
1 | Это таблица
2 | для логической
3 | репликации
4 | .
5 | Проверка репликации.
(5 rows)

Вставленная в таблицу строка на сервере, где работает публикация, с помощью логической репликации была доставлена подписчику. Подключившись к серверу на порт 6432 (на котором запущена подписка) можно убедиться в том, что строка, вставленная на стороне публикатора, доставлена подписчику.

Проверка состояния подписки

repbase=# SELECT * FROM pg_stat_subscription \gx
-[ RECORD 1 ]---------+------------------------------
subid | 24822
subname | sub_info
pid | 3492
relid |
received_lsn | 0/D6ED080
last_msg_send_time | 2024-11-26 07:43:45.649792+03
last_msg_receipt_time | 2024-11-26 07:43:45.649828+03
latest_end_lsn | 0/D6ED080
latest_end_time | 2024-11-26 07:43:45.649792+03

Системное представление pg_stat_subscription содержит сведения о текущем состоянии подписки.

Проверить, в каком состоянии пребывает процесс репликации на подписчике, можно, используя системное представление pg_stat_subscription.

В частности, в информации из этого представления можно узнать PID рабочего процесса логической репликации, который работает на стороне подписки.

https://www.postgresql.org/docs/15/monitoring-stats.html#MONITORING-PG-STAT-SUBSCRIPTION.

Процессы публикации и подписки

repbase=# \! ps f -C postgres
PID TTY STAT TIME COMMAND
3377 ? Ss 0:00 /usr/pangolin-6.2.0/bin/postgres -D /pgdata/06/data
3401 ? Ss 0:00 \_ postgres: checkpointer
3402 ? Ss 0:00 \_ postgres: background writer
3404 ? Ss 0:00 \_ postgres: idle sessions terminator
3405 ? Ss 0:00 \_ postgres: walwriter
3406 ? Ss 0:00 \_ postgres: license checker
3407 ? Ss 0:00 \_ postgres: autovacuum launcher
3408 ? Ss 0:00 \_ postgres: autounite launcher
3409 ? Ss 0:00 \_ postgres: integrity check launcher
3410 ? Ss 0:00 \_ postgres: logical replication launcher
3493 ? Ss 0:00 \_ postgres: walsender postgres [local] START_REPLICATION
2961 ? Ss 0:00 /usr/pangolin-6.2.0/bin/postgres -D /var/lib/postgres/rpl_pgdata
2963 ? Ss 0:00 \_ postgres: checkpointer
2964 ? Ss 0:00 \_ postgres: background writer
2966 ? Ss 0:00 \_ postgres: idle sessions terminator
2969 ? Ss 0:00 \_ postgres: integrity check launcher
2970 ? Ss 0:00 \_ postgres: license checker
2921 ? Ss 0:00 \_ postgres: walwriter
3022 ? Ss 0:00 \_ postgres: autovacuum launcher
3023 ? Ss 0:00 \_ postgres: autounite launcher
3024 ? Ss 0:00 \_ postgres: logical replication launcher
3492 ? Ss 0:00 \_ postgres: logical replication worker for subscription 24822
3516 ? Ss 0:00 \_ postgres: postgres repbase [local] idle

В примере на слайде на стороне публикатора запущен процесс 44154 walsender postgres, для которого отображено его состояние START_REPLICATION. Он с помощью потока репликации передает декодированные данные процессу 44153 logical replication worker, запущенному на стороне подписки. Номер подписки 34308 соответствует subid подписки.

repbase=# SELECT * FROM pg_stat_subscription \gx
-[ RECORD 1]---------+------------------------------
subid | 34308
subname | sub_info
pid | 44153
relid |
received_lsn | 0/C774928
last_msg_send_time | 2024-11-04 20:30:08.097156+03
last_msg_receipt_time| 2024-11-04 20:30:08.097208+03
latest_end_lsn | 0/C774928

Удаление подписки и публикации

repbase=# DROP SUBSCRIPTION sub_info ;
NOTICE: dropped replication slot "sub_info" on publisher
DROP SUBSCRIPTION
repbase=# \c - - - 5432
You are now connected to database "repbase" as user "postgres" via socket in "/tmp" at port "5432".
repbase=# DROP PUBLICATION pub_info ;
DROP PUBLICATION
  • Удаляет подписку команда DROP SUBSCRIPTION, ее необходимо выполнить первой.
  • Публикацию удаляет команда DROP PUBLICATION.

Если логическая репликация далее не требуется, надо вначале удалить подписку командой DROP SUBSCRIPTION, а затем можно удалить публикацию DROP PUBLICATION. https://www.postgresql.org/docs/15/sql-dropsubscription.html.

https://www.postgresql.org/docs/15/sql-droppublication.html.

Итоги

  • Репликация - процесс передачи данных с одного сервера СУБД на другой.
  • Репликация лежит в основе построения высокодоступных и высокопроизводительных компьютерных кластеров.
  • Физическая репликация основана на передаче записей WAL, она требует бинарной совместимости серверов и может реплицировать только весь кластер данных целиком.
  • Передачу WAL в физической репликации можно выполнять потоком или же с помощью файлового архива сегментов WAL.
  • В физической репликации поток данных однонаправленный от мастера к реплике.
  • Логическая репликация передает данные о командах INSERT, UPDATE, DELETE и TRUNCATE, выполненных с конкретными таблицами.
  • Логическая репликация гибче, не требует бинарной совместимости и позволяет выполнять разнонаправленную передачу данных.