Настройка Read Replicas для масштабирования чтения базы данных
Read Replicas — реплики базы данных, которые принимают только SELECT-запросы. Позволяют горизонтально масштабировать читающую нагрузку без изменения архитектуры приложения.
Сценарии применения
- Раздельная нагрузка: API-запросы читают из реплик, транзакции пишут в мастер
- Аналитические запросы (сложные JOIN, агрегации) идут на выделенную реплику
- Разные приложения используют разные реплики (CMS-бэкенд, публичный API, отчёты)
PostgreSQL: несколько реплик
# Создание реплики pg_basebackup
pg_basebackup -h master-host -U replication_user \
-D /var/lib/postgresql/14/main -P -Xs -R
# postgresql.auto.conf создан автоматически с primary_conninfo
# Дополнительные настройки для реплик
# postgresql.conf на репликах
hot_standby = on
hot_standby_feedback = on # предотвращает vacuum-конфликты
max_standby_archive_delay = 30s
max_standby_streaming_delay = 30s
Для аналитической реплики можно задать другие настройки памяти:
# На аналитической реплике
work_mem = 256MB # больше памяти для сортировок
effective_cache_size = 8GB
random_page_cost = 1.1 # быстрое SSD хранилище
Маршрутизация в приложении
Laravel (PHP)
// config/database.php
'pgsql' => [
'read' => [
['host' => '192.168.1.11'],
['host' => '192.168.1.12'], // Laravel балансирует случайно
],
'write' => [
'host' => '192.168.1.10',
],
'sticky' => true, // после записи следующий read из мастера
// ...
]
Node.js (Sequelize)
const sequelize = new Sequelize('myapp', 'user', 'password', {
dialect: 'postgres',
replication: {
read: [
{ host: '192.168.1.11', port: 5432 },
{ host: '192.168.1.12', port: 5432 },
],
write: { host: '192.168.1.10', port: 5432 },
},
pool: { max: 10, idle: 30000, acquire: 60000 },
})
pgBouncer в режиме read/write split
# pgbouncer.ini
[databases]
myapp = host=192.168.1.10 port=5432 dbname=myapp
myapp_read = host=192.168.1.11,192.168.1.12 port=5432 dbname=myapp
[pgbouncer]
pool_mode = transaction
max_client_conn = 500
default_pool_size = 25
Управление лагом репликации
Критическая проблема: запись → немедленное чтение может прочитать старые данные, если реплика отстаёт.
-- На мастере: отправить LSN клиенту
SELECT pg_current_wal_lsn();
-- На реплике: дождаться синхронизации до нужного LSN
SELECT pg_wal_lsn_diff(pg_last_wal_replay_lsn(), $1) >= 0;
-- Если false — переключиться на мастер
Паттерн в приложении:
def read_after_write(lsn):
replica = get_replica()
if replica.is_caught_up(lsn):
return replica.execute(query)
else:
return master.execute(query)
AWS RDS Read Replicas
Если используется управляемая БД:
aws rds create-db-instance-read-replica \
--db-instance-identifier myapp-replica-1 \
--source-db-instance-identifier myapp-master \
--db-instance-class db.r6g.large \
--availability-zone eu-west-1b \
--publicly-accessible false
Route 53 с латентной маршрутизацией для глобальных реплик: пользователи в ЕС читают из eu-west-1, в США — из us-east-1.
Мониторинг реплик
-- На мастере: статус всех реплик
SELECT application_name, client_addr, state,
sent_lsn, replay_lsn,
round(EXTRACT(EPOCH FROM write_lag)) as write_lag_sec,
round(EXTRACT(EPOCH FROM flush_lag)) as flush_lag_sec,
round(EXTRACT(EPOCH FROM replay_lag)) as replay_lag_sec
FROM pg_stat_replication;
Алерты через Prometheus + postgres_exporter:
- alert: ReplicaLag
expr: pg_replication_lag > 30
for: 5m
labels:
severity: warning
Срок выполнения
Настройка двух Read Replicas с балансировкой в приложении — 1–2 рабочих дня.







