lenec ru

← все посты

PostgreSQL logical replication: настройка, мониторинг и подводные камни

15K

Логическая репликация в PostgreSQL — механизм выборочного копирования данных между серверами на уровне отдельных таблиц. В отличие от физической репликации, которая передаёт WAL-сегменты целиком, логическая декодирует изменения и применяет их на подписчике. Это открывает сценарии: частичная репликация, мажорные апгрейды без даунтайма, консолидация данных из нескольких кластеров.

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

  • Физическая — побайтовая копия WAL. Реплика идентична мастеру. Нельзя реплицировать отдельные таблицы. Реплика read-only.
  • Логическая — декодирование WAL в логические операции (INSERT, UPDATE, DELETE). Подписчик может иметь свои индексы, дополнительные таблицы, другую мажорную версию PostgreSQL.

Типичные use cases:

  • Миграция между мажорными версиями (PG 14 → PG 16) с минимальным даунтаймом
  • Репликация подмножества таблиц в аналитический кластер
  • Консолидация данных из нескольких источников
  • Географически распределённые системы

Настройка publisher/subscriber пошагово

Два сервера: db-primary (publisher) и db-replica (subscriber). PostgreSQL 15+.

Конфигурация publisher

# postgresql.conf (publisher)
wal_level = logical
max_replication_slots = 10
max_wal_senders = 10
# pg_hba.conf
host    mydb    repl_user    10.0.1.0/24    scram-sha-256

Создаём пользователя и публикацию:

CREATE ROLE repl_user WITH REPLICATION LOGIN PASSWORD 'strong_pass_here';
GRANT SELECT ON ALL TABLES IN SCHEMA public TO repl_user;

-- Публикация всех таблиц
CREATE PUBLICATION my_pub FOR ALL TABLES;

-- Или выборочно
CREATE PUBLICATION orders_pub FOR TABLE orders, order_items, customers;

Конфигурация subscriber

На подписчике таблицы должны существовать заранее (DDL не реплицируется). Создаём структуру и подписку:

CREATE SUBSCRIPTION my_sub
    CONNECTION 'host=db-primary port=5432 dbname=mydb user=repl_user password=strong_pass_here'
    PUBLICATION my_pub;

PostgreSQL автоматически создаст replication slot на publisher, выполнит начальную синхронизацию и перейдёт в режим стриминга.

Проверка

-- На publisher: слоты
SELECT slot_name, active, restart_lsn FROM pg_replication_slots;

-- На subscriber: статус
SELECT * FROM pg_stat_subscription;

DDL-изменения и ограничения

Главное ограничение — DDL не реплицируется. Добавили колонку на publisher — подписчик не узнает. Порядок безопасного изменения:

  1. Добавить колонку на subscriber (с DEFAULT или NULL)
  2. Добавить колонку на publisher
  3. Репликация продолжит работать

Другие ограничения:

  • Sequences не синхронизируются — обновляйте через setval()
  • Large Objects не реплицируются
  • Таблицы без PRIMARY KEY — UPDATE/DELETE не пройдут без REPLICA IDENTITY
-- Если нет PK, задаём REPLICA IDENTITY
ALTER TABLE events REPLICA IDENTITY USING INDEX events_unique_idx;
-- Крайний вариант (дорого):
ALTER TABLE events REPLICA IDENTITY FULL;

Мониторинг лага

-- На publisher: лаг по подписчикам
SELECT
    client_addr,
    application_name,
    sent_lsn,
    replay_lsn,
    pg_wal_lsn_diff(sent_lsn, replay_lsn) AS replay_lag_bytes
FROM pg_stat_replication;

-- Размер удерживаемого WAL в слоте
SELECT
    slot_name,
    pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS wal_retained
FROM pg_replication_slots;

Отслеживайте wal_retained — если растёт, подписчик не успевает или отключился.

Типичные проблемы

Slot bloat — разрастание WAL

Неактивный подписчик удерживает WAL от удаления. Диск publisher может закончиться:

-- Неактивные слоты
SELECT slot_name, pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS retained
FROM pg_replication_slots WHERE NOT active;

-- Удаляем мёртвый слот
SELECT pg_drop_replication_slot('dead_slot');

В PostgreSQL 13+ задайте max_slot_wal_keep_size — лимит WAL на слот. При превышении слот инвалидируется.

Конфликты на subscriber

Если на subscriber есть строка с тем же PK — возникнет конфликт. Решения:

  • Удалить конфликтующую строку
  • В PG 15+: ALTER SUBSCRIPTION my_sub SKIP (lsn = '0/12345678');
  • Отключить/включить подписку после ручного исправления

Failover publisher

При failover (Patroni, pg_auto_failover) replication slot теряется. Варианты:

  • PostgreSQL 16+: failover slots переносятся автоматически
  • До PG 16: пересоздать подписку с copy_data = false
-- Пересоздание после failover (PG < 16)
DROP SUBSCRIPTION my_sub;
CREATE SUBSCRIPTION my_sub
    CONNECTION 'host=new-primary port=5432 dbname=mydb user=repl_user password=...'
    PUBLICATION my_pub
    WITH (copy_data = false);

Рекомендации

  • Всегда задавайте PRIMARY KEY или REPLICA IDENTITY
  • Мониторьте pg_replication_slots — неактивные слоты убивают диск
  • Настройте max_slot_wal_keep_size как страховку
  • DDL-миграции — сначала subscriber, потом publisher
  • Тестируйте failover-сценарии заранее

Логическая репликация требует внимания к DDL-миграциям и мониторинга слотов. Но при грамотной настройке это надёжный инструмент для zero-downtime миграций и построения распределённых систем на PostgreSQL.

Комментарии 0

  • Будьте первым, кто оставит комментарий.

Войдите, чтобы оставить комментарий.