Выберите продукт

PostgreSQL replication lag: причины и диагностика (WAL, I/O, autovacuum, network)

Реплика PostgreSQL отстаёт не только из‑за сети. Разберём, как правильно измерять replication lag через pg_stat_replication и LSN, отличать receive от replay, и быстро находить причины: чекпоинты и WAL, дисковый I/O, конфликты hot standby, autovacuum и TCP.
PostgreSQL replication lag: причины и диагностика (WAL, I/O, autovacuum, network)

Что такое replication lag в PostgreSQL и почему «секунды» обманывают

Replication lag — это разница между тем, что уже зафиксировано на primary, и тем, что успела получить и/или применить реплика. В PostgreSQL почти всегда речь о потоке WAL (Write-Ahead Log): primary генерирует WAL-записи, реплика их получает (receive) и проигрывает (replay). Если любой этап замедляется — растёт lag.

В быту lag часто измеряют «в секундах», но это не универсальная метрика. «Секунды» в статистике — это производная от временных отметок и подтверждений, а не ответ на вопрос «сколько времени нужно, чтобы догнать при текущей скорости». На всплесках нагрузки lag может расти рывками, а затем быстро схлопываться.

Правильная схема: сначала понять, на каком участке появляется отставание (отправка/сеть, запись WAL на диске, replay на standby), затем подтвердить это метриками, и только после этого менять параметры вроде max_wal_size или «крутить» autovacuum.

Быстрая модель: где появляется lag

Для потоковой репликации (streaming replication) конвейер выглядит так:

  1. Primary генерирует WAL при любом изменении данных.

  2. WAL sender отправляет WAL по сети на standby.

  3. Standby принимает WAL (процесс walreceiver) и пишет его в WAL-сегменты.

  4. Standby проигрывает WAL (recovery/replay), применяя изменения к данным.

На практике полезно держать в голове два «класса» отставания:

  • Send/Network lag: standby не успевает получить WAL от primary.

  • Replay/Apply lag: WAL получен, но standby медленно применяет его (обычно I/O на данных, CPU, блокировки или конфликты hot standby).

Этапы репликации PostgreSQL: send, write/flush и replay с отметками LSN

Диагностика шаг 1: измеряем lag через pg_stat_replication (primary)

На primary самый информативный источник — pg_stat_replication. Он показывает LSN на разных стадиях и оценку lag по времени для write/flush/replay.

SELECT
  pid,
  application_name,
  client_addr,
  state,
  sync_state,
  write_lag,
  flush_lag,
  replay_lag,
  sent_lsn,
  write_lsn,
  flush_lsn,
  replay_lsn
FROM pg_stat_replication
ORDER BY application_name;

Как читать ключевые поля:

  • sent_lsn — сколько primary уже отправил реплике.

  • write_lsn — сколько реплика приняла и записала в WAL (запись могла быть без fsync).

  • flush_lsn — сколько реплика гарантированно сбросила на диск (fsync).

  • replay_lsn — сколько реплика уже применила к данным (replay).

  • write_lag, flush_lag, replay_lag — оценка «времени отставания» по стадиям (может быть NULL).

Быстрая интерпретация:

  • Если sent_lsn заметно впереди write_lsn — проблема на пути «primary → сеть → walreceiver → запись WAL на standby».

  • Если write_lsn близко к sent_lsn, но replay_lsn сильно позади — WAL «доезжает», но реплика медленно применяет изменения (replay bottleneck).

Переводим LSN в «байты отставания»

Для инцидентов и мониторинга байты часто полезнее «секунд»: сразу видно, копится ли очередь WAL и какого она размера.

SELECT
  application_name,
  pg_size_pretty(pg_wal_lsn_diff(sent_lsn, replay_lsn)) AS behind_replay,
  pg_size_pretty(pg_wal_lsn_diff(sent_lsn, write_lsn))  AS behind_write
FROM pg_stat_replication;

Если на primary идёт «заливка» данных, lag в секундах может выглядеть страшно. А LSN diff в байтах покажет, что реплика догоняет приемлемой скоростью (или наоборот — очередь растёт).

FastFox VDS
Облачный VDS-сервер в России
Аренда виртуальных серверов с моментальным развертыванием инфраструктуры от 195₽ / мес

Диагностика шаг 2: проверяем состояние на standby

На реплике важно отличить «не получаем WAL» от «получаем, но не успеваем проигрывать». Начните с двух проверок: recovery-режим и живость replay.

SELECT
  pg_is_in_recovery() AS is_standby,
  now() - pg_last_xact_replay_timestamp() AS replay_delay;

Если pg_last_xact_replay_timestamp() возвращает NULL, это означает, что транзакции ещё не проигрывались. Причины бывают нормальные (свежий standby, нет транзакций) и проблемные (WAL не поступает или replay не движется).

Затем посмотрите walreceiver:

SELECT
  status,
  receive_start_lsn,
  written_lsn,
  flushed_lsn,
  latest_end_lsn,
  latest_end_time
FROM pg_stat_wal_receiver;

Если статус streaming и latest_end_time обновляется, канал жив. Тогда фокус смещается на replay (диск/CPU/конфликты).

Причина №1: WAL, чекпоинты и удержание сегментов

WAL — главный «конвейер» репликации. Он же источник типовых сюрпризов: волнообразный lag, внезапный рост I/O и накопление сегментов на primary.

Чекпоинты и «пилообразная» нагрузка

Слишком частые или тяжёлые чекпоинты вызывают всплески записи на диск. Это может замедлять и генерацию/отправку WAL на primary, и replay на standby (диск занят фоновой записью и fsync).

Проверьте статистику чекпоинтов:

SELECT
  checkpoints_timed,
  checkpoints_req,
  checkpoint_write_time,
  checkpoint_sync_time,
  buffers_checkpoint,
  buffers_clean,
  maxwritten_clean
FROM pg_stat_bgwriter;

Если checkpoints_req быстро растёт — чекпоинты часто «вынужденные» (например, упираетесь в max_wal_size). В таком режиме lag действительно может расти «волнами».

Роль max_wal_size в lag

max_wal_size не «ускоряет репликацию» напрямую. Он задаёт верхнюю границу объёма WAL между чекпоинтами. Слишком малое значение приводит к частым forced checkpoints и лишнему I/O, что снижает пропускную способность всего конвейера.

Имеет смысл думать про увеличение max_wal_size, если одновременно видите:

  • частые forced checkpoints (рост checkpoints_req);

  • пики write latency на диске;

  • рост lag синхронно с чекпоинтами (по логам и метрикам).

Replication slots и накопление WAL

Если lag большой или реплика недоступна, на primary может начать копиться WAL (особенно при использовании replication slots). Контролируйте удержание:

SELECT
  slot_name,
  slot_type,
  active,
  restart_lsn,
  confirmed_flush_lsn,
  pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS retained
FROM pg_replication_slots
ORDER BY retained DESC;

Большое значение retained — сигнал, что primary вынужден хранить много WAL из‑за слота (часто потому что реплика «умерла» или сильно отстала).

Причина №2: I/O — узкое место и на primary, и на standby

Replication lag часто оказывается не «про PostgreSQL», а про диск. Причём профили нагрузки различаются:

  • Primary: последовательная запись WAL + fsync, плюс фоновые записи грязных страниц (checkpointer/bgwriter).

  • Standby: запись WAL + заметный random I/O при replay (применение изменений к данным и индексам), иногда одновременно с чтением (hot standby).

Иллюстрация узкого места по диску: рост latency и отставание replay на standby

Признаки I/O bottleneck

  • lag растёт на массовых UPDATE/DELETE/INSERT на primary;

  • на standby растёт replay_lag, при этом write_lsn почти догнал sent_lsn;

  • в системе растёт iowait и latency диска (и это коррелирует по времени с ростом LSN diff).

Какие метрики в PostgreSQL смотреть

  • pg_stat_io (в новых версиях) — подробная телеметрия I/O по типам операций;

  • pg_stat_bgwriter — косвенно показывает давление чекпоинтов;

  • log_checkpoints — быстро показывает, сколько реально «стоит» чекпоинт по времени и объёму записи.

На уровне ОС смотрите iostat/pidstat/iotop. Главное — не «вообще высокая нагрузка», а совпадение по времени: рост lag и рост latency на диске.

Причина №3: сеть и поведение TCP

Сеть чаще виновата, когда отстаёт стадия receive: sent_lsn заметно впереди write_lsn, а на standby walreceiver либо получает медленно, либо реконнектится.

Что проверить в первую очередь

  • потери пакетов и ретрансмиты (обычно «плавающий» lag);

  • проблемы MTU/PMTU (чаще встречается в туннелях и при смешанных сетях);

  • конкуренцию по полосе (например, бэкапы или параллельная репликация).

Если сеть нестабильна, репликация может выглядеть «почти нормальной» по среднему lag, но при сбоях резко накапливать WAL и потом долго догонять. Поэтому важно смотреть не только текущие значения, но и динамику на графиках.

Причина №4: hot standby конфликты и длинные запросы на реплике

Hot standby позволяет читать с реплики, пока она применяет WAL. Но чтение на реплике может мешать replay. Типичный сценарий: на реплике выполняется длинный SELECT, а primary в это время активно делает UPDATE/DELETE или VACUUM. Для применения WAL standby нужно удалить/почистить версии строк, а запрос удерживает старый snapshot — возникает конфликт восстановления (recovery conflict).

Как это проявляется

  • replay_lag растёт, при этом write_lsn/flush_lsn близки к sent_lsn;

  • в логах standby появляются сообщения про recovery conflict и отмену запросов;

  • или наоборот: запросы не отменяются, но replay «ждёт» (зависит от настроек задержек).

Какие настройки влияют

  • hot_standby_feedback — снижает конфликты, но может усиливать bloat на primary (включайте осознанно);

  • max_standby_streaming_delay и max_standby_archive_delay — сколько standby готов терпеть конфликт до отмены запроса.

Практика: если реплика нужна и для HA (минимальный lag), и для тяжёлой аналитики, лучше разделять роли (отдельная read-only реплика под отчёты) или жёстко ограничивать длительность запросов на HA-реплике.

Причина №5: autovacuum и побочные эффекты для репликации

autovacuum редко является «первопричиной lag», но часто усиливает нагрузку: влияет на объём WAL и I/O, и иногда становится триггером конфликтов на hot standby.

В активных таблицах autovacuum может:

  • генерировать дополнительный WAL (особенно при freeze/anti-wraparound);

  • занимать I/O и CPU, конкурируя с replay на standby;

  • провоцировать recovery conflicts при длинных чтениях на реплике.

Как понять, что autovacuum участвует

Сначала посмотрите, где больше всего мёртвых строк и когда был последний vacuum:

SELECT
  relname,
  n_dead_tup,
  last_autovacuum,
  last_vacuum,
  last_autoanalyze,
  vacuum_count,
  autovacuum_count
FROM pg_stat_user_tables
ORDER BY n_dead_tup DESC
LIMIT 20;

Если n_dead_tup стабильно большое, autovacuum не справляется. Это почти всегда приводит к росту нагрузки при UPDATE/DELETE, а значит — косвенно увеличивает и replication lag (WAL и I/O).

Дальше имеет смысл проверить, не упираетесь ли вы в лимиты фоновых процессов:

  • autovacuum_max_workers

  • autovacuum_vacuum_cost_limit и autovacuum_vacuum_cost_delay

  • табличные storage parameters для крупных «горячих» таблиц

Виртуальный хостинг FastFox
Виртуальный хостинг для сайтов
Универсальное решение для создания и размещения сайтов любой сложности в Интернете от 95₽ / мес

Практический чек-лист: как локализовать причину lag за 15 минут

1) На primary: определяем тип отставания

  1. Снимите pg_stat_replication (LSN и lag-поля).

  2. Посчитайте отставание в байтах через pg_wal_lsn_diff.

  3. Проверьте слоты (pg_replication_slots) и удержание WAL.

2) На standby: отличаем receive от replay

  1. pg_stat_wal_receiver: статус streaming, обновляется ли latest_end_time?

  2. now() - pg_last_xact_replay_timestamp(): replay реально «живой»?

3) Сопоставляем с системой

  • Если отстаёт receive: сеть, лимиты канала, запись WAL на диск standby.

  • Если отстаёт replay: диск с данными на standby, hot standby конфликты, конкуренция с запросами, CPU.

  • Если lag растёт «волнами»: чекпоинты, max_wal_size, бурсты I/O.

Что менять в настройках и в каком порядке (без «магических» твиков)

1) Уберите очевидные I/O проблемы

  • Проверьте, что primary и standby не делят один и тот же медленный том с соседями по I/O.

  • Если standby используется для чтения, убедитесь, что запросы не «выжимают» диск в моменты, когда нужно быстро применять WAL.

2) Нормализуйте чекпоинты и WAL-режим

  • Включите log_checkpoints и измерьте реальное время checkpoint.

  • Оцените уместность увеличения max_wal_size, если forced checkpoints частые.

3) Разграничьте роли реплики

Одна из самых частых причин «необъяснимого» lag — попытка использовать одну реплику и как HA-standby, и как отчётную read-replica. Если нужен стабильный минимальный lag — выделяйте отдельную реплику под тяжёлые отчёты или ограничивайте длительность запросов на HA-реплике.

4) Autovacuum: лечим причину, а не симптом

Если autovacuum не успевает, начните с выявления «плохих» таблиц и паттернов нагрузки. Тюнинг autovacuum без понимания, что именно тормозит, иногда даёт обратный эффект: больше конкуренции за I/O и ещё больше WAL.

Типовые сценарии и быстрые выводы

Сценарий A: write_lsn далеко позади sent_lsn

Чаще всего это сеть или запись WAL на standby. Проверьте стабильность канала и скорость диска именно там, где лежит WAL у реплики.

Сценарий B: write_lsn ≈ sent_lsn, но replay_lsn сильно позади

Чаще всего реплика упирается в I/O на данных или в hot standby конфликты. Смотрите логи standby на recovery conflict и профиль нагрузки от чтения.

Сценарий C: lag растёт волнами и совпадает с пиками записи

Чаще всего это чекпоинты и ограничение по WAL (max_wal_size слишком мал для текущей нагрузки) в сочетании с медленным диском. Подтверждайте через pg_stat_bgwriter и log_checkpoints.

Дополнительно: логическая репликация и миграции

Если вы используете логическую репликацию (например, для миграций или частичной репликации таблиц), лаг и узкие места измеряются по-другому: там появляется очередь на уровне слотов/подписок и apply workers. В таком случае полезно свериться с отдельным разбором: логическая репликация PostgreSQL для миграций.

Заключение

PostgreSQL replication lag почти всегда имеет измеримую причину: либо WAL не успевает «доехать» (сеть/receive), либо не успевает «проиграться» (replay/I/O/конфликты hot standby), либо конвейер периодически «затыкают» чекпоинты и фоновые процессы. Начинайте с pg_stat_replication, переводите LSN в байты, отличайте receive и replay — и только потом принимайте решения про max_wal_size, политику чтения с реплики и настройку autovacuum.

Для стабильной работы реплик критично, чтобы под PostgreSQL было предсказуемое I/O. Если вам нужна изолированная производительность под базу, выбирайте VDS с гарантированными ресурсами и отдельными дисками под данные и WAL.

Поделиться статьей

Вам будет интересно

Debian/Ubuntu: конфликт systemd-resolved DNSStubListener на 127.0.0.53 с dnsmasq, Unbound и BIND OpenAI Статья написана AI (GPT 5)

Debian/Ubuntu: конфликт systemd-resolved DNSStubListener на 127.0.0.53 с dnsmasq, Unbound и BIND

Если локальный DNS в Debian или Ubuntu не стартует с ошибкой address already in use, причина часто в systemd-resolved и DNSStubLis ...
Debian/Ubuntu: как исправить NFS mount.nfs: access denied by server while mounting OpenAI Статья написана AI (GPT 5)

Debian/Ubuntu: как исправить NFS mount.nfs: access denied by server while mounting

Ошибка mount.nfs: access denied by server while mounting в Debian и Ubuntu обычно указывает на проблему на стороне NFS-сервера: не ...
Debian/Ubuntu: как устранить конфликт systemd-resolved DNSStubListener с BIND9, dnsmasq и AdGuard Home OpenAI Статья написана AI (GPT 5)

Debian/Ubuntu: как устранить конфликт systemd-resolved DNSStubListener с BIND9, dnsmasq и AdGuard Home

Если в Debian или Ubuntu DNS-сервер не стартует из-за ошибки port 53 busy, часто причина в systemd-resolved с локальным слушателем ...