Репликация в PostgreSQL
Предпосылки: репликация (sync/async, replication lag, failover, split brain, кворум), WAL, TCP (RTT).
← Диагностика медленных запросов | Шардирование →
Пока PostgreSQL живёт на одном сервере, он — единственная точка отказа. Сервер перезагружается, и на 30 секунд приложение не может ни читать, ни писать. Диск умирает — данные потеряны, если нет свежего бэкапа. Репликация решает обе проблемы: вторая (и третья) копия данных на другом сервере позволяет переключиться при сбое и разгрузить primary от части чтений.
Physical replication: standby как точная копия
Physical (streaming) replication строится вокруг WAL: primary пишет журнал, replica получает поток WAL-записей и воспроизводит те же изменения у себя. В результате на реплике — побайтовая копия всего кластера: все базы данных, все таблицы, все индексы.
Допустим, интернет-магазин работает на одном primary с 500 ГБ данных и 2000 запросов/сек. В 3:00 ночи сервер уходит на перезагрузку после обновления ядра ОС. Без реплики — 30–60 секунд простоя, потерянные запросы, таймауты в checkout. С hot standby приложение переключается на реплику: read-only запросы (каталог, поиск, профили) продолжают работать, а запись (заказы, оплата) недоступна до продвижения реплики в primary.
Failover: от обнаружения до переключения. Primary упал в 14:23:00. Patroni (или другой HA-менеджер) обнаруживает отсутствие heartbeat через 10 секунд. Проверяет кворум через etcd — это ещё 2–3 секунды. Продвигает реплику в primary — pg_promote() занимает < 1 секунды. Обновляет DNS/endpoint — 5–10 секунд. Итого: приложение видит новый primary через 20–30 секунд. Без автоматизации (ручной failover) — минуты, иногда десятки минут.
PostgreSQL как сервер умеет быть primary или standby, но автоматический failover «из коробки» не делает. Выбор кандидата, недопущение split brain и переключение клиентов реализуют внешние инструменты. Patroni делегирует выбор нового primary алгоритму консенсуса через etcd — это гарантирует, что два узла не станут primary одновременно.
Synchronous replication: цена гарантий
По умолчанию репликация асинхронная: primary подтверждает COMMIT, не дожидаясь реплики. При failover это создаёт окно потери: транзакции, подтверждённые клиенту, но не дошедшие до реплики. На нагруженной системе с 1000 транзакций/сек и лагом 100 мс — это до 100 потерянных транзакций.
Synchronous replication закрывает это окно: primary ждёт подтверждения от реплики перед ответом клиенту. Параметр synchronous_commit определяет, что считать подтверждением: remote_write (WAL записан в OS buffer на реплике, ~1 мс к latency), remote_apply (WAL применён, ~5–10 мс к latency). synchronous_standby_names задаёт, какие реплики участвуют.
Цена конкретна: каждый COMMIT дорожает на сетевой round-trip до реплики. При 1 мс RTT это +1 мс к каждому COMMIT. При 10 мс RTT (другой датацентр) — +10 мс, что для сервиса с target latency 50 мс — 20% бюджета. Если синхронная реплика недоступна — запись блокируется. Синхронная репликация сдвигает PostgreSQL в сторону CP: при потере реплики записи останавливаются, но данные не расходятся.
Replication slots: координация удаления WAL
Без явной координации primary удаляет старые WAL-сегменты по своему расписанию. Если реплика отстала или была временно недоступна — WAL, который ей нужен, уже удалён. Единственный выход — полный resync: скопировать весь кластер заново. Для базы в 500 ГБ это часы простоя реплики.
Replication slot — маркер, который говорит primary: «эти WAL-данные ещё не дошли до реплики, не удаляй их». Пока слот существует, primary сохраняет WAL от позиции, записанной в слоте.
Но эта защита создаёт обратную проблему: если реплика умерла навсегда, а слот остался — WAL копится на primary бесконечно. В production это регулярная причина «диск заполнен на 100%»: 500 ГБ WAL накопились за выходные, пока никто не заметил мёртвую реплику. Слоты нужно мониторить (pg_replication_slots) и удалять, когда они больше не нужны.
Logical replication: поток изменений выбранных таблиц
Logical replication передаёт не байты страниц, а изменения строк (INSERT/UPDATE/DELETE) для таблиц, включённых в publication. В отличие от physical, на принимающей стороне — обычный PostgreSQL, который может иметь свои таблицы, индексы, и даже другую версию PostgreSQL.
Типичные сценарии: реплицировать не весь кластер, а несколько таблиц в аналитическую базу; доставлять изменения в другой сервис как поток событий; выполнить миграцию на новую версию PostgreSQL с минимальным простоем.
Ограничения: DDL не реплицируется — схему нужно поддерживать согласованной отдельно. Последовательности (SEQUENCE) не синхронизируются автоматически.
Как узнать роль текущего сервера
SELECT pg_is_in_recovery(); -- true на реплике, false на primarySources
- PostgreSQL Documentation (пример: v16): High Availability, Warm Standby, Synchronous Replication, Replication Slots, Logical Replication. https://www.postgresql.org/docs/16/high-availability.html, https://www.postgresql.org/docs/16/logical-replication.html