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

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

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

Логическая репликация строится на передаче информации о выполненных в конкретных таблицах командах DML (INSERT, UPDATE, DELETE, TRUNCATE). В отличие от физической репликации, в логической не используются понятия «мастер» и «реплика» — вместо этого взаимодействие организовано через публикации и подписки. На одном сервере могут существовать одновременно и публикации, и подписки, поэтому потоки данных могут быть разнонаправленными.

Ключевые объекты логической репликации:

  • Публикация (Publication) — объект базы данных, создаваемый для одной или нескольких таблиц. Публикация извлекает из записей WAL команды DML, относящиеся к публикуемым таблицам, и передает декодированные данные подписчикам. При создании можно указать конкретные таблицы, выбрать требуемые столбцы или настроить фильтрацию строк;
  • Подписка (Subscription) — объект базы данных, подключающийся к одной или нескольким публикациям. Подписка получает декодированные сообщения по протоколу репликации и применяет их к таблицам на стороне подписчика.

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

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

  • Поток логической репликации использует обычный протокол репликации, изначально спроектированный для физической репликации. Однако по протоколу передаются не сырые записи WAL, а декодированные команды DML;
  • Процесс wal sender на стороне публикации получает декодированные изменения из журнала WAL и отправляет их подписчику по сетевому протоколу репликации;
  • На стороне подписки процесс logical replication launcher сообщает postmaster о необходимости породить процесс logical replication worker, который обслуживает подписку — получает декодированные команды и применяет их к целевым таблицам.

Подготовка сервера​

Для логической репликации сервер публикатора должен записывать в журнал WAL информацию, достаточную для декодирования команд DML. Это обеспечивается параметром wal_level, которому необходимо присвоить значение logical. Кроме того, файл pg_hba.conf должен содержать правила аутентификации для подключений репликации.

Переключимся на сервер публикатора (порт 5432) и установим требуемое значение.

postgres@repbase=# \c - - - 5432
You are now connected to database "repbase" as user "postgres" via socket in "/tmp" at
port "5432".

Выполним изменение параметра командой ALTER SYSTEM, которая запишет значение в файл postgresql.auto.conf.

postgres@repbase=# ALTER SYSTEM SET wal_level TO logical;
ALTER SYSTEM

Проверим текущее значение параметра.

postgres@repbase=# \dconfig+ wal_level
List of configuration parameters
Parameter | Value | Type | Context | Access privileges
-----------+---------+------+------------+-------------------
wal_level | replica | enum | postmaster |
(1 row)

Текущее значение остается replica, поскольку параметр имеет контекст postmaster — изменение вступает в силу после перезапуска сервера.

Действия для перезапуска сервера

Выйти из сеанса psql командой \q.

postgres@repbase=# \q

Выйти из оболочки пользователя postgres командой exit, вернувшись к пользователю student.

[postgres@ServerName ~]$ exit

Перезапустить экземпляр PostgreSQL командой sudo systemctl restart postgresql (пользователь student имеет права sudo, пользователь postgres — нет, что соответствует требованиям безопасности).

[student@ServerName ~]$ sudo systemctl restart postgresql

Вернуться под пользователя postgres командой sudo -iu postgres и подключиться к базе repbase.

[student@ServerName ~]$ sudo -iu postgres

Теперь снова подключимся к серверу публикатору.

[postgres@ServerName ~]$ psql -d repbase -q
postgres@repbase=# \dconfig+ wal_level
List of configuration parameters
Parameter | Value | Type | Context | Access privileges
-----------+---------+------+------------+-------------------
wal_level | logical | enum | postmaster |
(1 row)

Сервер перезапущен. Подключение к базе repbase на порту 5432 подтверждает, что экземпляр работает с новым значением wal_level = logical.

Организация репликации​

На этом этапе формируются объекты репликации. Сначала на сервере-источнике создается публикация, которая определяет, какие таблицы будут реплицироваться. Затем структура таблиц переносится на сервер-получатель, чтобы подписчик знал схему данных. После этого создается подписка — она устанавливает связь между серверами и запускает начальную синхронизацию данных.

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

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

postgres@repbase=# CREATE TABLE info2pub(id integer PRIMARY KEY, msg text);
CREATE TABLE
примечание

Таблица, участвующая в логической репликации, должна иметь первичный ключ или настроенный параметр REPLICA IDENTITY. Без него обновления и удаления строк не будут реплицироваться корректно.

postgres@repbase=# INSERT INTO info2pub VALUES(1,'Это таблица'),(2,'для логической'),(3,'репликации'),(4,'.');
INSERT 0 4

Создадим публикацию с помощью команды CREATE PUBLICATION для таблицы info2pub.

postgres@repbase=# CREATE PUBLICATION pub_info FOR TABLE info2pub;
CREATE PUBLICATION

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

Проверить параметры созданной публикации можно метакомандой \dRp.

postgres@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)

При создании публикации можно задать параметр publish, который позволяет ограничить набор реплицируемых команд DML (insert, update, delete, truncate). В нашем случае он указан не был, поэтому в списке публикаций для каждой команды DML стоит значение t (true).

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

Перед созданием подписки на сервере подписчика должна существовать таблица с такой же структурой, как на стороне публикатора. Скопируем структуру таблицы info2pub с помощью утилиты pg_dump.

[postgres@ServerName ~]$ pg_dump -d repbase -t info2pub --schema-only | psql -d repbase -p 6432
Разбор команды

  • pg_dump -d repbase — логическое резервное копирование базы данных repbase (подключение на порту по умолчанию 5432);
  • опция -t info2pub — выбор конкретной таблицы info2pub;
  • опция --schema-only — экспорт только структуры (команды DDL), без данных;
  • результат передается по каналу в psql -d repbase -p 6432, который подключается к базе repbase на сервере подписчика (порт 6432) и выполняет полученные команды DDL.

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

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

Таблица info2pub на стороне подписчика имеет ту же структуру, что и на публикаторе: те же столбцы, типы данных и первичный ключ. Данные пока отсутствуют — таблица пуста.

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

Создадим подписку с помощью команды CREATE SUBSCRIPTION, которая подключится к публикации pub_info на сервере публикатора. Строка подключения CONNECTION указывает целевой сервер и базу данных, параметр PUBLICATION — перечень публикаций для подписки.

postgres@repbase=# CREATE SUBSCRIPTION sub_info CONNECTION 'dbname=repbase' PUBLICATION pub_info;
NOTICE: created replication slot "sub_info" on publisher
CREATE SUBSCRIPTION

Сведения о созданных подписках выводятся метакомандой \dRs.

postgres@repbase=# \x \dRs+ \x
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

Expanded display is off.

Сообщение NOTICE: created replication slot "sub_info" on publisher подтверждает, что на стороне публикатора автоматически создан слот репликации sub_info.

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

Начальная загрузка данных выполняется по умолчанию — отключить это поведение можно параметром copy_data=false команды CREATE SUBSCRIPTION.

Проверим подключение к серверу подписчика (порт 6432) и данные таблицы info2pub.

postgres@repbase=# \conninfo
You are connected to database "repbase" as user "postgres" via socket in "/tmp" at port "6432".

Выполним выборку данных из таблицы info2pub.

postgres@repbase=# SELECT * FROM info2pub;
id | msg
----+----------------
1 | Это таблица
2 | для логической
3 | репликации
4 | .
(4 rows)

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

Мониторинг репликации​

Начальная загрузка данных подтверждает, что подписка создана и работает, однако для системного контроля репликации этого недостаточно. Чтобы обнаружить задержки, рассинхронизацию или сбои потока репликации, необходим мониторинг. Он осуществляется с помощью системных представлений, отражающих состояние подписки на стороне подписчика, а рабочие процессы репликации наблюдаются через представления на обоих серверах.

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

Подключимся к серверу публикатора и вставим новую строку в таблицу info2pub.

postgres@repbase=# \c - - - 5432
You are now connected to database "repbase" as user "postgres" via socket in "/tmp" at port "5432".
postgres@repbase=# INSERT INTO info2pub VALUES (5,'Проверка репликации.');
INSERT 0 1

Переключимся на сервер подписчика (порт 6432) и проверим, что новая строка была реплицирована.

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

Строка с id = 5, вставленная на стороне публикатора, успешно реплицирована на подписчика. Это подтверждает корректную работу логической репликации: изменения, примененные к публикуемой таблице, передаются подписчику по протоколу репликации в виде декодированных команд DML.

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

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

Проверим состояние подписки sub_info.

postgres@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

  • subname — имя подписки;
  • pid — PID рабочего процесса логической репликации на стороне подписчика. Если значение пустое, рабочий процесс не запущен;
  • received_lsn — последний LSN (Log Sequence Number) полученного от публикатора сообщения;
  • last_msg_send_time — время отправки последнего сообщения от подписчика к публикатору;
  • last_msg_receipt_time — время получения последнего сообщения от публикатора;
  • latest_end_lsn — LSN последней примененной транзакции;
  • latest_end_time — время завершения применения последней транзакции.

Если pid не пустой, подписка активна и рабочий процесс (logical replication worker) обрабатывает изменения. Совпадение received_lsn и latest_end_lsn указывает на отсутствие задержек в применении данных.

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

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

postgres@repbase=# \! ps f -C postgres
PID TTY STAT TIME COMMAND
3377 ? Ss 0:00 /usr/pangolin-{pangolin_version}/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-{pangolin_version}/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
Ключевые процессы логической репликации

  • walsender postgres [local] START_REPLICATION (PID 3493) — процесс на стороне публикатора (-D /pgdata/06/data). Передает подписчику декодированные команды DML по протоколу репликации;
  • logical replication worker for subscription 24822 (PID 3492) — процесс на стороне подписчика (-D /var/lib/postgres/rpl_pgdata). Принимает декодированные изменения и применяет их к целевым таблицам. Номер подписки 24822 соответствует значению subid из представления pg_stat_subscription;
  • logical replication launcher — процесс, присутствующий на обоих серверах. Следит за состоянием подписок и при необходимости создает рабочие процессы logical replication worker.

Сопоставим PID рабочего процесса с данными из pg_stat_subscription — совпадение значений подтвердит, что представление корректно отражает фактическое состояние процессов сервера.

postgres@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

Поле pid (44153) совпадает с PID процесса logical replication worker в дереве процессов.

Удаление объектов репликации​

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

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

Удалим подписку sub_info на сервере подписчика с помощью команды DROP SUBSCRIPTION.

postgres@repbase=# DROP SUBSCRIPTION sub_info;
NOTICE: dropped replication slot "sub_info" on publisher
DROP SUBSCRIPTION
примечание

При удалении подписки автоматически удаляется слот репликации на стороне публикатора, что подтверждается сообщением NOTICE: dropped replication slot "sub_info" on publisher. Это освобождает ресурсы и позволяет серверу архивировать сегменты WAL, ранее удерживаемые слотом.

Переключимся на сервер публикатора и удалим публикацию с помощью команды DROP PUBLICATION.

postgres@repbase=# \c - - - 5432
You are now connected to database "repbase" as user "postgres" via socket in "/tmp" at port "5432".
postgres@repbase=# DROP PUBLICATION pub_info;
DROP PUBLICATION

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

Итоги​

  • Публикация (CREATE PUBLICATION) создается на сервере-источнике для одной или нескольких таблиц;
  • Таблица должна иметь первичный ключ или настроенный параметр REPLICA IDENTITY для корректной репликации обновлений и удалений;
  • Подписка (CREATE SUBSCRIPTION) создается на сервере-получателе и подключается к публикации по строке CONNECTION;
  • При создании подписки автоматически создается слот репликации на стороне публикатора;
  • Начальная загрузка данных выполняется автоматически — отключается параметром copy_data=false;
  • Состояние подписки проверяется через представление pg_stat_subscription (поля received_lsn, latest_end_lsn, pid);
  • На подписчике работает процесс logical replication worker, на публикаторе — walsender с декодированными командами DML;
  • Параметр publish в CREATE PUBLICATION ограничивает набор реплицируемых команд DML;
  • Удаление объектов: сначала DROP SUBSCRIPTION на подписчике (слот удаляется автоматически), затем DROP PUBLICATION на публикаторе.

Самопроверка​

Вопрос 1

Какой параметр wal_level требуется для логической репликации, и что нужно сделать после его изменения?

Вопрос 2

Какие требования к таблице, участвующей в логической репликации? Выберите все верные варианты.

Вопрос 3

Что делает опция --schema-only в команде pg_dump?

Вопрос 4

Что происходит при создании подписки (CREATE SUBSCRIPTION)? Выберите все верные варианты.

Вопрос 5

Какие ключевые поля содержит представление pg_stat_subscription на стороне подписчика? Выберите все верные варианты.

Вопрос 6

Какой процесс работает на стороне подписчика и применяет декодированные изменения к целевым таблицам?

Вопрос 7

В какой последовательности нужно удалять объекты логической репликации? Выберите все верные варианты.

Вопрос 8

Какой параметр отключает начальную загрузку данных при создании подписки?