# Физическая (потоковая) репликация _Репликация · PostgreSQL Knowledge Base_ **TL;DR:** Standby проигрывает у себя тот же WAL, что пишет primary, и получается побайтовая копия кластера. Primary отдаёт журнал процессом walsender, standby принимает walreceiver'ом и применяет startup-процессом. Лаг меряется в LSN: насколько replay на standby отстал от записи на primary. ## Идея в одну строку Primary уже пишет всё, что меняет данные, в журнал предзаписи (WAL, см. [wal](/courses/postgres/kb/wal.md)). Раз журнала достаточно, чтобы поднять кластер после сбоя, его достаточно и чтобы держать вторую копию в актуальном состоянии. Standby просто не прекращает recovery: он бесконечно проигрывает приходящий WAL. Поэтому физическая репликация - это не «копирование строк», а «проигрывание журнала на втором сервере». Копия получается побайтовая: те же файлы, те же relfilenode, тот же системный идентификатор кластера. ## Кто что делает ``` primary standby ┌──────────────┐ ┌──────────────┐ │ backends │ пишут WAL │ walreceiver │ принимает поток │ ↓ │ │ ↓ │ │ wal на диск │ ──walsender──▶ │ wal на диск │ │ │ (по TCP) │ ↓ │ └──────────────┘ │ startup │ проигрывает (replay) └──────────────┘ ``` - **walsender** на primary читает WAL и шлёт его по сети. - **walreceiver** на standby принимает поток и сохраняет на диск. - **startup** на standby проигрывает записи в том же порядке. Standby при этом доступен на чтение (`hot_standby = on`), но любая попытка записи отвергается: кластер в режиме recovery. ## Три LSN и лаг LSN (Log Sequence Number) - это позиция в журнале, монотонно растущее число. Лаг репликации - это разница LSN между тем, что primary уже записал, и тем, на какой позиции standby. `pg_stat_replication` на primary показывает по каждому standby три рубежа: - `sent_lsn` - досюда primary отправил; - `flush_lsn` - досюда standby сбросил на диск; - `replay_lsn` - досюда standby реально проиграл (и видно на чтении). Разрыв между `flush_lsn` и `replay_lsn` - это «WAL принят, но ещё не применён». Под нагрузкой replay отстаёт первым: запись на диск дёшева, а проигрывание конкурирует с читающими запросами. ```sql SELECT application_name, state, pg_wal_lsn_diff(sent_lsn, replay_lsn) AS replay_bytes_behind, replay_lag FROM pg_stat_replication; ``` ## Слоты репликации Без слота primary не знает, докуда standby дочитал, и удаляет старый WAL по своим правилам (`max_wal_size`, checkpoint, см. [checkpoint](/courses/postgres/kb/checkpoint.md)). Если standby отстал больше, чем primary хранил, нужного сегмента уже нет - репликация рвётся с «requested WAL segment has already been removed». Слот (`pg_create_physical_replication_slot`) чинит это: primary держит WAL ровно до `restart_lsn` слота и не удаляет раньше. Цена - обратная: забытый неактивный слот удержит WAL навсегда и забьёт диск primary. Поэтому слоты надо мониторить через `pg_replication_slots.active`. ## Синхронность По умолчанию репликация асинхронная: COMMIT на primary возвращается, не дожидаясь standby. Это быстро, но при падении primary последние транзакции могут не успеть уехать. `synchronous_standby_names` + `synchronous_commit = on` заставят COMMIT ждать подтверждения standby - ноль потерь ценой задержки каждого COMMIT. Связано с [hot-standby-feedback](/courses/postgres/kb/hot-standby-feedback.md) (как чтение на standby влияет на vacuum мастера) и [logical-replication](/courses/postgres/kb/logical-replication.md) (репликация по строкам, а не по байтам журнала). ## Команды ```sql SELECT * FROM pg_stat_replication; ``` Состояние всех подключённых standby с primary: state, lsn'ы, лаг ```sql SELECT pg_create_physical_replication_slot('standby1'); ``` Создать физический слот - primary будет держать WAL до restart_lsn ```sql SELECT slot_name, active, restart_lsn, wal_status FROM pg_replication_slots; ``` Слоты и докуда каждый держит WAL; неактивный слот опасен для диска ```sql SELECT pg_current_wal_lsn(); ``` Текущая позиция записи WAL на primary - точка отсчёта для лага ## См. также - [hot_standby_feedback и vacuum мастера](/courses/postgres/kb/hot-standby-feedback.md) - [Логическая репликация (publication/subscription)](/courses/postgres/kb/logical-replication.md) - [Распределённые ловушки: 2PC, multi-master, CAP](/courses/postgres/kb/distributed-pitfalls.md) - [WAL: принцип write-ahead и LSN](/courses/postgres/kb/wal.md) - [Уровни журнала: minimal, replica, logical](/courses/postgres/kb/wal-level.md)