Репликация PostgreSQL позволяет создавать копии базы данных в реальном времени. Используется для отказоустойчивости (HA), горизонтального масштабирования чтения и резервного копирования.
Типы репликации
Streaming replication (физическая)
├── Асинхронная — primary не ждёт подтверждения standby (быстро, возможна потеря данных)
└── Синхронная — primary ждёт подтверждения (надёжно, +задержка)
Logical replication
└── Репликация на уровне строк (можно выбирать таблицы, разные версии PG)
Настройка: Primary сервер
postgresql.conf
# Включить WAL для репликации
wal_level = replica # минимум для streaming replication
# Сколько standby могут подключаться одновременно
max_wal_senders = 5
# Хранить WAL пока standby не получит
wal_keep_size = 1GB # или max_wal_size
# Синхронная репликация (опционально)
# synchronous_standby_names = 'standby1'
# Для горячего резерва (read queries на standby)
hot_standby = on
pg_hba.conf — разрешить репликацию
# Разрешить подключение standby-серверов для репликации
# TYPE DATABASE USER ADDRESS METHOD
host replication replicator 10.0.0.2/32 md5
host replication replicator 10.0.0.3/32 md5
Создать пользователя для репликации
CREATE USER replicator REPLICATION LOGIN PASSWORD 'strong_password';
-- REPLICATION — специальная роль, только для репликации
Настройка: Standby сервер
Клонировать Primary
# На standby-сервере: скопировать данные с primary
pg_basebackup \
-h 10.0.0.1 \ # IP primary
-U replicator \
-D /var/lib/postgresql/data \ # директория данных standby
-P \ # прогресс
-Xs \ # включить WAL в бэкап (streaming)
-R # создать standby.signal + postgresql.auto.conf
Флаг -R автоматически создаёт:
standby.signal— файл, который говорит PostgreSQL работать в режиме standbypostgresql.auto.conf— параметры подключения к primary
postgresql.auto.conf (создаётся автоматически)
primary_conninfo = 'host=10.0.0.1 port=5432 user=replicator password=strong_password application_name=standby1'
Запустить Standby
# Запустить PostgreSQL — он автоматически войдёт в режим recovery
systemctl start postgresql
# Проверить статус
psql -c "SELECT pg_is_in_recovery();"
-- t ← standby в режиме recovery (read-only)
Мониторинг репликации
На Primary
-- Статус всех подключённых standby
SELECT
application_name,
state, -- streaming / catchup
sent_lsn, -- отправлено primary
write_lsn, -- записано на standby
flush_lsn, -- сброшено на диск standby
replay_lsn, -- применено standby
pg_wal_lsn_diff(sent_lsn, replay_lsn) AS lag_bytes,
write_lag, -- задержка записи
flush_lag, -- задержка сброса
replay_lag -- задержка применения
FROM pg_stat_replication;
На Standby
-- Статус standby
SELECT * FROM pg_stat_wal_receiver;
-- Отставание в байтах
SELECT pg_wal_lsn_diff(pg_last_wal_receive_lsn(), pg_last_wal_replay_lsn()) AS lag_bytes;
-- Время последнего применённого WAL
SELECT now() - pg_last_xact_replay_timestamp() AS lag_seconds;
Read Replicas: маршрутизация запросов
# Django: несколько баз данных
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'HOST': '10.0.0.1', # Primary (write)
'NAME': 'myapp',
...
},
'replica': {
'ENGINE': 'django.db.backends.postgresql',
'HOST': '10.0.0.2', # Standby (read)
'NAME': 'myapp',
...
}
}
DATABASE_ROUTERS = ['myapp.routers.PrimaryReplicaRouter']
# routers.py
class PrimaryReplicaRouter:
def db_for_read(self, model, **hints):
return 'replica' # Читать с реплики
def db_for_write(self, model, **hints):
return 'default' # Писать в primary
def allow_relation(self, obj1, obj2, **hints):
return True
def allow_migrate(self, db, app_label, model_name=None, **hints):
return db == 'default' # Миграции только на primary
Синхронная репликация
# postgresql.conf на Primary
synchronous_standby_names = 'FIRST 1 (standby1, standby2)'
# или
synchronous_standby_names = 'standby1'
# ANY — хотя бы один из standby должен подтвердить
synchronous_standby_names = 'ANY 1 (standby1, standby2)'
-- Уровни синхронности для транзакции
SET synchronous_commit = on; -- подождать flush на standby (безопасно)
SET synchronous_commit = remote_write; -- подождать write (быстрее, менее надёжно)
SET synchronous_commit = local; -- не ждать standby (асинхронно)
SET synchronous_commit = off; -- даже не ждать local flush
Логическая репликация
-- PRIMARY: создать publication
CREATE PUBLICATION mypub FOR TABLE users, orders;
-- или все таблицы:
CREATE PUBLICATION mypub FOR ALL TABLES;
-- STANDBY/другой сервер: создать subscription
CREATE SUBSCRIPTION mysub
CONNECTION 'host=10.0.0.1 dbname=myapp user=replicator password=...'
PUBLICATION mypub;
-- Проверить статус
SELECT * FROM pg_stat_subscription;
Когда использовать логическую репликацию:
- Разные версии PostgreSQL (например, миграция с PG 14 на PG 16)
- Нужно реплицировать только часть таблиц
- Двунаправленная репликация (multi-master)
- ETL в аналитическую БД
Промоция Standby (failover)
# Ручная промоция standby в primary
pg_ctl promote -D /var/lib/postgresql/data
# или
psql -c "SELECT pg_promote();"
# Проверить что стал primary
psql -c "SELECT pg_is_in_recovery();"
-- f ← теперь primary
Для автоматического failover используют:
- Patroni — высокодоступный кластер с автофейловером
- repmgr — управление репликацией и фейловером
- pg_auto_failover — от Microsoft, простая настройка
Docker Compose пример
services:
postgres-primary:
image: postgres:16
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: secret
POSTGRES_DB: myapp
command: >
postgres
-c wal_level=replica
-c max_wal_senders=5
-c hot_standby=on
postgres-standby:
image: postgres:16
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: secret
depends_on:
- postgres-primary
# Требует дополнительных скриптов для pg_basebackup и standby.signal
Итог
| Streaming (физическая) | Logical | |
|---|---|---|
| Копирует | Весь кластер | Выбранные таблицы |
| Разные версии PG | ❌ | ✅ |
| DDL | Автоматически | ❌ (ручное) |
| Задержка | Минимальная | Чуть больше |
| Применение | Только PG | Любая СУБД |
Streaming replication — стандарт для HA и read replicas. Логическая репликация — для специальных сценариев: миграция версий, частичная репликация, multi-master.