Логическая репликация и кластерные решения

Рейтинг: 62.1% · 15 голосов
Подробный курс по PostgreSQL: SQL по демобазе, типы данных и JSONB, индексы, транзакции и MVCC, VACUUM, EXPLAIN и планировщик, роли и привилегии, репликация, бэкап, настройка под нагрузку и под 1С. Актуально на 2026.
Ответить
Аватара пользователя
Egor_DBA
Сообщения: 47
Зарегистрирован: 11 май 2026, 05:31

Логическая репликация и кластерные решения

Сообщение Egor_DBA »

Оглавление курса (47)
  1. Что такое PostgreSQL: история, философия, где применяется
  2. Урок 1. Что нового в PostgreSQL 15 и далее (15 -> 16 -> 17)
  3. Установка и первый запуск: Linux, Windows, Docker
  4. psql детально: метакоманды и работа в консоли
  5. Демобаза Авиаперевозки Postgres Pro: структура и развёртывание
  6. SELECT: проекция, фильтрация, сортировка
  7. Транзакции и ACID
  8. Уровни изоляции транзакций: Read Committed, Repeatable Read, Serializable
  9. Соединения таблиц (JOIN)
  10. Агрегация: GROUP BY, HAVING, GROUPING SETS
  11. Подзапросы и CTE (WITH), рекурсия
  12. Оконные функции
  13. Изменение данных: INSERT/UPDATE/DELETE, UPSERT, MERGE
  14. Числовые и булевы типы, NULL и трёхзначная логика
  15. Строки, текст, дата и время, таймзоны
  16. Массивы, диапазоны, enum, составные типы
  17. JSON и JSONB: операторы и индексация
  18. DDL: CREATE TABLE, схемы, ALTER
  19. Ограничения целостности
  20. Представления и материализованные представления
  21. Последовательности, IDENTITY, генерируемые столбцы
  22. Индексы: B-tree и когда индекс не используется
  23. Типы индексов: Hash, GiST, SP-GiST, GIN, BRIN
  24. Продвинутые индексы: частичные, по выражению, покрывающие
  25. Обслуживание индексов и таблиц: bloat, REINDEX, CONCURRENTLY
  26. Полнотекстовый поиск
  27. Роли и пользователи
  28. Привилегии: GRANT/REVOKE
  29. Row-Level Security и безопасность данных
  30. Подключение приложения: pg_hba.conf, SSL, пулы соединений
  31. Серверное программирование: функции и процедуры
  32. Триггеры и события
  33. Архитектура PostgreSQL: процессы и память
  34. MVCC: версии строк и видимость
  35. VACUUM, autovacuum, freeze и wraparound
  36. WAL, контрольные точки и долговечность
  37. Блокировки и взаимоблокировки
  38. Планировщик запросов
  39. EXPLAIN и EXPLAIN ANALYZE: чтение планов
  40. Конфигурация сервера: ключевые параметры
  41. Настройка под нагрузку и под 1С
  42. Параллельные запросы и партиционирование
  43. Резервное копирование и восстановление
  44. Репликация: потоковая и hot standby
  45. Логическая репликация и кластерные решения (вы здесь)
  46. Внешние данные, расширения и сертификация
  47. Разбор планов запросов на explain.tensor.ru: глубокое чтение EXPLAIN ANALYZE
Урок 44. Логическая репликация и кластерные решения

В прошлых уроках мы разбирали физическую репликацию: реплика побайтово повторяет WAL основного сервера и остаётся его точной копией. Это надёжно, но негибко - нельзя выбрать отдельные таблицы, нельзя реплицировать между разными версиями PostgreSQL, нельзя писать на реплику. Логическая репликация снимает эти ограничения: она передаёт не блоки данных на диске, а сами строки - что вставили, что обновили, что удалили. В этом уроке разберём механику PUBLICATION/SUBSCRIPTION, типовые сценарии (миграция с минимальным простоем, разнос данных по узлам) и пройдёмся по экосистеме высокой доступности: Patroni, Citus, pgpool. Цель - понять не только как настроить репликацию в postgresql, но и когда какой инструмент брать.

Изображение

Как это работает

В основе логической репликации лежит механизм logical decoding. Сервер-источник читает свой WAL, но не отдаёт сырые байты, а прогоняет их через плагин-декодер pgoutput, который превращает низкоуровневые записи в понятные события уровня строк: INSERT таблицы X с такими-то значениями, UPDATE по такому-то ключу. Эти события публикуются через объект PUBLICATION.

На стороне получателя создаётся SUBSCRIPTION. Она открывает специальный слот репликации на источнике (чтобы тот не удалял ещё не доставленный WAL) и запускает два фоновых процесса: walsender на источнике и apply worker на приёмнике. Apply worker применяет полученные изменения обычными INSERT/UPDATE/DELETE - именно поэтому таблицы на двух серверах могут физически отличаться: разный набор индексов, разные неучаствующие столбцы, даже разная мажорная версия PostgreSQL.

Ключевое требование - идентификация строк. Чтобы применить UPDATE и DELETE, приёмнику нужно понять, какую именно строку трогать. По умолчанию используется первичный ключ. Если PK нет, придётся задать REPLICA IDENTITY FULL, и тогда в WAL пишется вся старая версия строки целиком - это дорого и медленно. Поэтому таблицы без первичного ключа - главный источник боли в логической репликации.

Важно понимать границы: логическая репликация переносит DML, но не переносит DDL. Создание новой таблицы, добавление колонки, новый индекс - всё это нужно накатывать на приёмник руками или своей оснасткой. Также по умолчанию не реплицируются TRUNCATE до явного указания и не передаются последовательности (sequence), то есть значения IDENTITY и serial на приёмнике придётся выставлять отдельно при переключении.

SQL и примеры

Возьмём демобазу Авиаперевозки. Допустим, аналитике нужны только две таблицы - flights и airports - на отдельном сервере, а не вся база.

На сервере-источнике публикуем нужные таблицы:

Код: Выделить всё

-- публикуем только две таблицы, а не всю базу
CREATE PUBLICATION analytics_pub
  FOR TABLE bookings.flights, bookings.airports;

-- посмотреть, что входит в публикацию
SELECT * FROM pg_publication_tables
 WHERE pubname = 'analytics_pub';
На сервере-приёмнике заранее должны существовать таблицы с совместимой структурой (хотя бы нужные столбцы и тот же первичный ключ). Затем подписываемся:

Код: Выделить всё

-- приёмник подключается к источнику и тянет данные
CREATE SUBSCRIPTION analytics_sub
  CONNECTION 'host=src_host dbname=demo user=repl password=secret'
  PUBLICATION analytics_pub;
При создании подписки по умолчанию выполняется начальная синхронизация - копируется текущее содержимое таблиц, а потом поток переключается на инкрементальные изменения. Проверить состояние можно так:

Код: Выделить всё

-- на приёмнике: статус воркеров подписки
SELECT subname, srrelid::regclass, srsubstate
  FROM pg_subscription_rel r
  JOIN pg_subscription s ON s.oid = r.srsubid;
-- srsubstate: 'r' = готова и реплицируется

-- на источнике: отставание подписчика в байтах
SELECT application_name,
       pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn) AS lag_bytes
  FROM pg_stat_replication;
Можно публиковать не все строки. С PostgreSQL 15 появились фильтры строк и списки столбцов - реплицируем только рейсы определённого аэропорта:

Код: Выделить всё

-- только вылеты из Шереметьево (PG 15+)
CREATE PUBLICATION svo_pub
  FOR TABLE bookings.flights
  WHERE (departure_airport = 'SVO');
Сценарий миграции с минимальным простоем выглядит так: поднимаем новый сервер (можно сразу PostgreSQL 17), переносим схему через pg_dump --schema-only, настраиваем PUBLICATION на старом и SUBSCRIPTION на новом. Логическая репликация догоняет данные в фоне, приложение продолжает работать. В момент переключения останавливаем запись, ждём нулевого отставания, выставляем sequence через setval и переводим трафик на новый сервер. Простой - минуты вместо часов.

Частые грабли
  • Таблица без первичного ключа: UPDATE/DELETE либо не реплицируются, либо требуют REPLICA IDENTITY FULL с просадкой производительности.
  • Забыли про DDL: добавили колонку на источнике, на приёмнике её нет - apply worker встаёт с ошибкой, и слот начинает копить WAL, заполняя диск источника.
  • Брошенная подписка: удалили приёмник, но слот на источнике остался. Неактивный слот удерживает WAL бесконечно. Чистите через pg_drop_replication_slot.
  • Конфликты на приёмнике: если в реплицируемую таблицу кто-то пишет локально, может возникнуть нарушение уникальности и репликация остановится.
  • Последовательности не едут сами - после переключения IDENTITY и serial на новом сервере отстают, и новые вставки ломаются по дубликату ключа.
  • wal_level должен быть logical, max_replication_slots и max_wal_senders заданы с запасом, иначе подписка просто не стартует.
  • До PostgreSQL 16 реплику нельзя было использовать как источник публикации, а слоты не переживали failover - это исправили в 16 и 17 соответственно.
Мини-лаба

Понадобятся два кластера PostgreSQL (можно два экземпляра на разных портах).
  • На обоих в postgresql.conf выставьте wal_level = logical и перезапустите серверы.
  • На источнике создайте таблицу orders (id int primary key, amount numeric) и наполните парой строк.
  • На приёмнике создайте таблицу orders с такой же структурой, но пустую.
  • На источнике выполните CREATE PUBLICATION p1 FOR TABLE orders.
  • На приёмнике создайте SUBSCRIPTION на источник и проверьте, что начальные строки скопировались.
  • Вставьте новую строку на источнике и убедитесь, что она появилась на приёмнике через секунду.
  • Удалите таблицу orders на приёмнике (не удалив подписку) и посмотрите в логах источника, как растёт неактивный слот - затем аккуратно уберите подписку и слот.
Контрольные вопросы
  • Чем логическая репликация принципиально отличается от физической и какие новые возможности это даёт?
  • Почему таблице нужна REPLICA IDENTITY и когда приходится ставить FULL?
  • Что произойдёт со слотом репликации, если подписчик надолго пропадёт, и чем это грозит источнику?
  • Какие объекты НЕ переносятся логической репликацией автоматически и как это учесть при миграции?
  • В каких задачах вы выберете Patroni, а в каких Citus, и почему это разные инструменты?
  • Что изменилось в логической репликации в PostgreSQL 16 и 17 относительно версии 15?
👍1 ❤️1 🔥 😄 🤔2
Аватара пользователя
vaultmaster
Сообщения: 1
Зарегистрирован: 04 июн 2026, 18:24

Re: Логическая репликация и кластерные решения

Сообщение vaultmaster »

А если на приёмнике в реплицируемую таблицу случайно вставит локальное приложение - оно правда уронит всю подписку по дублю ключа? как от этого обычно защищаются, права на запись отбирают?
👍1 ❤️ 🔥1 😄 🤔
Аватара пользователя
sereniti
Сообщения: 1
Зарегистрирован: 15 май 2026, 09:58

Re: Логическая репликация и кластерные решения

Сообщение sereniti »

Сделал миграцию через logical на pg17, всё догналось, но забыл про setval на sequence - первые же вставки легли по duplicate key. Добавьте в чеклист жирным, реально частые грабли.
👍 ❤️ 🔥 😄 🤔
Ответить
← Предыдущая глава
Репликация: потоковая и hot standby
Следующая глава →
Внешние данные, расширения и сертификация

Все главы курса «PostgreSQL: от первого запроса до продакшена»

Поделиться темой: ✈ Telegram VK
  • Похожие темы

Вернуться в «PostgreSQL: от первого запроса до продакшена»

Кто сейчас на конференции

Сейчас этот форум просматривают: Amazon [Bot] и 2 гостя