Раздел 33 · PostgreSQL с нуля

Репликация: потоковая и логическая

junior~20 мин

без входаВойди, чтобы отмечать прогресс, вести заметки и решать задачи в редакторе. войти

В прошлом уроке мы резали большую таблицу на секции, но все они остались на одном сервере. Если у этого сервера сгорит диск, магазин встанет, а заказы за последние часы могут пропасть. Сегодня заводим серверу копию, которая всё время повторяет за ним каждое изменение.

Партия по записи ходов

Шахматисты записывают партию: «e4, e5, Кf3…». По такой записи на любой другой доске можно повторить партию ход за ходом и получить ту же позицию. Не нужно фотографировать доску после каждого хода: достаточно передавать ходы.

У PostgreSQL такая запись уже есть. Это журнал упреждающей записи, WAL. Любое изменение сначала попадает в журнал и только потом в файлы таблиц. Зачем это нужно для восстановления после сбоя, рассказано в уроке про журнал и восстановление. А раз все изменения уже записаны по порядку, их можно отправить на второй сервер, и тот повторит их у себя. Это и есть репликация.

Журнал можно потрогать прямо в песочнице. У каждого места в журнале есть адрес, LSN: номер байта от начала журнала. Запомним адрес, поменяем цену одной книги и посмотрим, на сколько вырос журнал:

CREATE TEMP TABLE wal_mark AS SELECT pg_current_wal_insert_lsn() AS lsn;
UPDATE books SET price = price + 10 WHERE id = 1;
SELECT pg_wal_lsn_diff(pg_current_wal_insert_lsn(), lsn) AS wal_bytes FROM wal_mark;
 wal_bytes
-----------
        72
(1 row)

Изменение одной строки заняло в журнале 72 байта, а UPDATE всех девяти книг дал 648. Ровно эти байты ведущий сервер и отправляет копиям.

Потоковая репликация

Самый частый вид называют потоковой или физической. В ней два участника:

  • Ведущий сервер, primary. Принимает все записи и рассылает журнал.
  • Реплика, standby. Получает журнал и повторяет его у себя.
  приложение ── INSERT, UPDATE ──▶ ведущий ──── поток WAL ────▶ реплика 1
                                     │                             (только чтение)
                                     └────────── поток WAL ────▶ реплика 2

Реплика повторяет журнал байт в байт, поэтому получается точная копия всего кластера: все базы, все таблицы, индексы и роли. Отсюда два свойства:

  • Версия одна и та же. Ведущий и реплика должны быть одной основной версии PostgreSQL, на одной архитектуре процессора.
  • Писать в реплику нельзя. Иначе её копия разошлась бы с журналом ведущего. Читать можно, это включено по умолчанию настройкой hot_standby.

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

ERROR:  cannot execute INSERT in a read-only transaction

Узнать, кто перед тобой, можно функцией pg_is_in_recovery(): на реплике она возвращает true, ведь реплика всё время «восстанавливается» по чужому журналу. В песочнице сервер один и он ведущий, поэтому там false.

Как поднять реплику

В песочнице второго сервера нет, поэтому здесь только команды. Сначала на ведущем заводят роль с правом репликации, как в уроке про роли:

CREATE ROLE replicator WITH REPLICATION LOGIN PASSWORD 'secret';

и разрешают ей подключаться для репликации в pg_hba.conf, который мы видели во втором уроке:

host replication replicator 10.0.0.0/24 scram-sha-256

Потом на будущей реплике копируют каталог данных ведущего утилитой pg_basebackup:

pg_basebackup -h 10.0.0.1 -U replicator \
  -D /var/lib/postgresql/18/main \
  -X stream -R -P
  • -D каталог, куда лягут данные. Он должен быть пустым.
  • -X stream параллельно с копированием файлов забирает журнал, чтобы копия получилась целой.
  • -R записывает настройки подключения к ведущему и файл standby.signal, по которому сервер при старте поймёт, что он реплика.
  • -P показывает прогресс.

После этого реплику запускают, и она сама подключается к ведущему и начинает получать журнал.

урок из курса

Урок из курса «PostgreSQL с нуля». Курс покупается один раз и остаётся навсегда: 500 ₽.

Оплата пока не подключена: премиум и курсы открываются промокодом.