В прошлом уроке мы резали большую таблицу на секции, но все они остались на одном сервере. Если у этого сервера сгорит диск, магазин встанет, а заказы за последние часы могут пропасть. Сегодня заводим серверу копию, которая всё время повторяет за ним каждое изменение.
Партия по записи ходов
Шахматисты записывают партию: «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 ₽.
Оплата пока не подключена: премиум и курсы открываются промокодом.