Как осуществить потоковую репликацию в PostgreSQL
Потоковая репликация имеет два аспекта: доставка журналов и синхронизация баз данных. Доставка журналов - основной аспект потоковой репликации, поскольку основной сервер отправляет данные WAL на подключенные резервные серверы при каждой записи. Синхронизация базы данных необходима для синхронной репликации, при которой основной сервер обменивается данными с каждым резервным сервером для синхронизации их кластеров баз данных.
Чтобы понять, как работает потоковая репликация, необходимо разобраться в том, как основной сервер управляет несколькими резервными серверами. В этом разделе мы начнем с простого случая (т. е. с системы с одним первичным сервером и одним резервным.
Связь между основным и синхронным резервным сервером
В потоковой репликации связь между основным сервером и синхронным резервным сервером играет жизненно важную роль. Рассмотрим процесс передачи данных при фиксации транзакции.
- Бэкенд-процесс на основном сервере записывает и сбрасывает данные WAL в файл сегмента WAL.
- Процесс walsender отправляет записанные данные WAL процессу walreceiver.
- Бэкенд-процесс ожидает ответа ACK от резервного сервера, получая защелку с помощью SyncRepWaitForLSN().
- Walreceiver записывает полученные данные WAL в сегмент WAL резервного сервера и отправляет ответ ACK walsender.
- Walreceiver сбрасывает данные WAL в сегмент и уведомляет процесс запуска об обновленных данных WAL.
- Startup процесс воспроизводит записанные данные WAL.
- Walsender освобождает защелку внутреннего процесса после получения ответа ACK, завершая действие фиксации или отмены.
Во время этого процесса основной сервер получает ответы ACK от walreceiver, которые содержат такую информацию, как последние записанные, сброшенные и воспроизведенные номера LSN, а также временную отметку ответа. Эти ответы помогают основному серверу контролировать состояние всех подключенных резервных серверов.
XLogWalRcvSendReply
XLogWalRcvSendReply(void)@src/backend/replication/walreceiver.c
/* Construct a new message */
writePtr = LogstreamResult.Write;
flushPtr = LogstreamResult.Flush;
applyPtr = GetXLogReplayRecPtr(NULL);
resetStringInfo(&reply_message);
pq_sendbyte(&reply_message, 'r');
pq_sendint64(&reply_message, writePtr);
pq_sendint64(&reply_message, flushPtr);
pq_sendint64(&reply_message, applyPtr);
pq_sendint64(&reply_message, GetCurrentTimestamp());
pq_sendbyte(&reply_message, requestReply ? 1 : 0);
walreceiver возвращает ответы ACK не только при записи и сбросе данных WAL, но и в качестве пульса от резервного сервера. Таким образом, у основного сервера всегда есть понимание состояния всех подключенных резервных серверов.
Информацию о LSN, связанную с подключенными резервными серверами, можно отобразить с помощью запросов, показанных ниже:
testdb=# SELECT application_name AS host,
write_location AS write_LSN, flush_location AS flush_LSN,
replay_location AS replay_LSN FROM pg_stat_replication;
host | write_lsn | flush_lsn | replay_lsn
----------+-----------+-----------+------------
standby1 | 0/5000280 | 0/5000280 | 0/5000280
standby2 | 0/5000280 | 0/5000280 | 0/5000280
(2 rows)
Примечание: параметр wal_receiver_status_interval (integer)
Данный параметр пределяет минимальную частоту, с которой процесс, принимающий WAL на резервном сервере, будет сообщать о состоянии репликации вышестоящему резервному серверу. В этом сообщении передаются следующие позиции в журнале предзаписи: позиция изменений записанных, изменений, сохранённых на диске, и изменений применённых. Задать этот параметр можно только в postgresql.conf или в командной строке при запуске сервера. По умолчанию его значение равно 10 секундам.
Устранение сбоя синхронного резервного сервера
Когда синхронный резервный сервер выходит из строя и не может вернуть ответ ACK, основной сервер продолжает бесконечно ждать ответов. В результате выполняющиеся транзакции не могут быть зафиксированы, а обработка запросов останавливается. Другими словами, все операции основного сервера практически останавливаются. Потоковая репликация не обеспечивает автоматический переход в асинхронный режим по истечении времени ожидания.
Чтобы избежать этой ситуации, есть два подхода. Одним из них является использование нескольких резервных серверов для повышения доступности системы. Другой — вручную переключиться с синхронного на асинхронный режим, выполнив следующие действия:
1. Установите пустую строку для параметра synchronous_standby_names.
synchronous_standby_names = ''
2. Выполните команду pg_ctl с параметром перезагрузки.
postgres> pg_ctl -D $PGDATA reload
Описанная выше процедура не влияет на подключенных клиентов. Первичный сервер продолжает обработку транзакций, и все сеансы между клиентами и соответствующими внутренними процессами сохраняются.




