Пошук уроків, статей та іншого контенту
Налаштуєте потокову фізичну реплікацію між сервером PostgreSQL і standby-вузлом та перевірите її стан.
Потокова реплікація PostgreSQL передає WAL-записи з основного сервера на standby-вузол у режимі реального часу.
Primary приймає записи від клієнтів і генерує WAL.
Standby отримує WAL через постійне з'єднання та відтворює зміни у власній копії даних.
Реплікація є фізичною: передаються зміни на рівні сторінок і WAL, а не SQL-команди.
Standby має бути сумісним із primary за основною версією PostgreSQL.
За замовчуванням це асинхронна реплікація: primary не чекає підтвердження застосування WAL standby-вузлом.
У цьому уроці використаємо таку схему:
Primary: 10.0.0.10:5432
Standby: 10.0.0.20:5432
Користувач реплікації: repl_user
Реплікаційний слот: standby_1IP-адреси замініть на адреси серверів у вашій мережі.
На primary виконайте:
ALTER SYSTEM SET wal_level = 'replica';
ALTER SYSTEM SET max_wal_senders = 10;
ALTER SYSTEM SET max_replication_slots = 10;Ці параметри потребують перезапуску PostgreSQL.
Основні параметри:
wal_level = replica — генерує WAL, достатній для фізичної реплікації.
max_wal_senders — максимальна кількість процесів, які надсилають WAL.
max_replication_slots — максимальна кількість реплікаційних слотів.
Перевірити поточні значення можна так:
SHOW wal_level;
SHOW max_wal_senders;
SHOW max_replication_slots;Перезапустіть PostgreSQL способом, який відповідає вашій системі:
sudo systemctl restart postgresqlПісля перезапуску перевірте, що параметри застосувалися:
SHOW wal_level;
SHOW max_wal_senders;
SHOW max_replication_slots;Primary має приймати TCP-з'єднання не лише з localhost.
Перевірте значення:
SHOW listen_addresses;Якщо сервер слухає тільки localhost, задайте адресу сервера або всі інтерфейси:
ALTER SYSTEM SET listen_addresses = '*';Після зміни listen_addresses потрібен перезапуск PostgreSQL:
sudo systemctl restart postgresqlУ production-середовищі краще вказувати конкретні адреси інтерфейсів, а не використовувати *.
На primary створіть окремого користувача:
CREATE ROLE repl_user
WITH LOGIN
REPLICATION
PASSWORD 'replace-with-a-strong-password';Користувачеві не потрібні права SUPERUSER. Для підключення з метою потокової реплікації достатньо атрибута REPLICATION.
Перевірити атрибути ролі:
\du repl_userpg_hba.confЗнайдіть шлях до файлу конфігурації:
SHOW hba_file;Додайте до pg_hba.conf правило, яке дозволяє standby підключатися до primary:
host replication repl_user 10.0.0.20/32 scram-sha-256Це правило означає:
підключення до спеціальної бази replication;
користувач — repl_user;
дозволена адреса — 10.0.0.20;
автентифікація — SCRAM-SHA-256.
Розміщуйте правило до загальніших або забороняючих правил, які можуть перехопити це підключення.
Перечитайте конфігурацію без перезапуску:
SELECT pg_reload_conf();Або:
sudo systemctl reload postgresqlПеревірити, чи PostgreSQL приймає з'єднання на потрібній адресі та порту:
SHOW listen_addresses;
SHOW port;Також firewall primary має дозволяти TCP-з'єднання з адреси standby на порт PostgreSQL, зазвичай 5432.
Реплікаційний слот гарантує, що primary не видалить WAL, який standby ще не встиг отримати.
Без слота primary може видалити старі WAL після досягнення wal_keep_size або внаслідок роботи архівації. Якщо standby буде недоступним достатньо довго, після відновлення йому може знадобитися нова базова копія.
Слот можна створити вручну:
SELECT pg_create_physical_replication_slot('standby_1');Перевірити слоти:
SELECT
slot_name,
slot_type,
active,
restart_lsn
FROM pg_replication_slots;У цьому уроці слот буде створений автоматично командою pg_basebackup з параметрами -C -S standby_1.
Реплікаційний слот утримує WAL на primary. Якщо standby втрачено надовго, WAL може зайняти весь доступний дисковий простір. Слоти потрібно контролювати.
На standby зупиніть PostgreSQL:
sudo systemctl stop postgresqlКаталог даних standby має бути порожнім перед виконанням pg_basebackup.
Дізнатися каталог даних можна так:
sudo -u postgres psql -Atc "SHOW data_directory;"Наприклад:
sudo -u postgres psql -Atc "SHOW data_directory;"Можливий результат:
/var/lib/postgresql/16/mainПереконайтеся, що вказуєте саме каталог даних standby, а не primary. Видалення вмісту каталогу даних є руйнівною операцією, тому перед очищенням перевірте шлях.
Щоб pg_basebackup і standby могли підключатися до primary без інтерактивного введення пароля, створіть файл .pgpass для системного користувача PostgreSQL:
sudo -u postgres sh -c 'printf "%s\n" "10.0.0.10:5432:replication:repl_user:replace-with-a-strong-password" > ~/.pgpass'
sudo chmod 600 ~postgres/.pgpassУ production-середовищі не зберігайте пароль у командній історії. Пароль у прикладі потрібно замінити на фактичний.
Шаблон рядка .pgpass має такий вигляд:
hostname:port:database:username:passwordМожна використовувати * замість назви бази:
10.0.0.10:5432:*:repl_user:replace-with-a-strong-passwordФайл має належати користувачеві, від імені якого запускається PostgreSQL, і мати права 0600.
Запустіть pg_basebackup на standby від імені користувача PostgreSQL:
sudo -u postgres pg_basebackup \
-h 10.0.0.10 \
-p 5432 \
-U repl_user \
-D /var/lib/postgresql/16/main \
-Fp \
-Xs \
-P \
-R \
-C \
-S standby_1Параметри команди:
-h — адреса primary;
-p — порт primary;
-U — користувач реплікації;
-D — каталог даних standby;
-Fp — звичайний формат копії файлів;
-Xs — передавати WAL потоково під час створення копії;
-P — показувати прогрес;
-R — створити конфігурацію для підключення до primary та файл standby.signal;
-C — створити реплікаційний слот;
-S standby_1 — ім'я слота.
Команда pg_basebackup повинна завершитися без помилок. Якщо каталог даних не порожній, PostgreSQL може відмовитися виконувати операцію.
Перевірте, що було створено файл сигналу:
sudo -u postgres test -f /var/lib/postgresql/16/main/standby.signal \
&& echo "standby.signal exists"У PostgreSQL 12 і новіших параметри підключення standby зберігаються в postgresql.auto.conf, а наявність standby.signal вмикає режим standby.
Перевірити значення primary_conninfo можна так:
sudo -u postgres grep '^primary_conninfo' \
/var/lib/postgresql/16/main/postgresql.auto.confОчікувано там будуть адреса primary, порт і користувач реплікації.
Запустіть PostgreSQL на standby:
sudo systemctl start postgresqlПеревірте стан сервісу:
sudo systemctl status postgresqlНа standby виконайте:
SELECT
pg_is_in_recovery() AS is_standby,
pg_last_wal_receive_lsn() AS received_lsn,
pg_last_wal_replay_lsn() AS replayed_lsn,
pg_last_xact_replay_timestamp() AS last_replayed_transaction;Для standby значення pg_is_in_recovery() має бути true.
На primary подивіться активні WAL-відправники:
SELECT
pid,
usename,
application_name,
client_addr,
state,
sync_state,
sent_lsn,
write_lsn,
flush_lsn,
replay_lsn,
write_lag,
flush_lag,
replay_lag
FROM pg_stat_replication;Для підключеного standby очікуються такі значення:
client_addr — адреса standby;
state — streaming;
sync_state — зазвичай async;
LSN-значення — позиції переданого та застосованого WAL.
Якщо pg_stat_replication не повертає рядків, standby не має активного реплікаційного підключення до цього primary.
Перевірте стан слота:
SELECT
slot_name,
active,
restart_lsn,
wal_status,
safe_wal_size
FROM pg_replication_slots
WHERE slot_name = 'standby_1';Після підключення standby значення active має бути true.
На standby доступна статистика WAL-приймача:
SELECT
status,
receive_start_lsn,
written_lsn,
flushed_lsn,
latest_end_lsn,
latest_end_time,
sender_host,
sender_port,
slot_name
FROM pg_stat_wal_receiver;Для робочої реплікації status зазвичай має значення streaming.
Корисно порівняти отриману та застосовану позиції:
SELECT
pg_last_wal_receive_lsn() AS received_lsn,
pg_last_wal_replay_lsn() AS replayed_lsn,
pg_wal_lsn_diff(
pg_last_wal_receive_lsn(),
pg_last_wal_replay_lsn()
) AS bytes_not_replayed;bytes_not_replayed показує, скільки байтів WAL уже отримано, але ще не застосовано. Невелика ненульова різниця може бути нормальною.
На primary створіть окрему таблицю та додайте запис:
CREATE TABLE replication_check (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
message text NOT NULL,
created_at timestamptz NOT NULL DEFAULT now()
);
INSERT INTO replication_check (message)
VALUES ('row created on primary');Переконайтеся, що транзакція завершена:
COMMIT;Якщо команди виконувалися в режимі автоматичного коміту, окремий COMMIT не потрібен.
На standby виконайте:
SELECT *
FROM replication_check;Рядок має бути доступним для читання.
Standby у режимі recovery не приймає звичайні операції запису:
INSERT INTO replication_check (message)
VALUES ('write on standby');Очікувано PostgreSQL поверне помилку про те, що сервер перебуває у режимі recovery.
На primary можна приблизно оцінити відставання standby в байтах:
SELECT
application_name,
client_addr,
state,
pg_size_pretty(
pg_wal_lsn_diff(
pg_current_wal_lsn(),
replay_lsn
)
) AS replay_lag_bytes
FROM pg_stat_replication;Цей показник порівнює поточну позицію WAL на primary з позицією, яку standby повідомив як застосовану.
На standby можна перевірити час останньої відтвореної транзакції:
SELECT
now() - pg_last_xact_replay_timestamp()
AS approximate_replay_delay;Якщо на primary давно не було транзакцій, значення може бути NULL. Це не обов'язково означає проблему реплікації.
Для правильної діагностики дивіться на кілька показників одночасно:
state у pg_stat_replication;
status у pg_stat_wal_receiver;
різницю між sent_lsn, flush_lsn і replay_lsn;
стан реплікаційного слота;
повідомлення у журналах PostgreSQL.
Перевірте:
Чи запущений PostgreSQL на primary.
Чи правильні адреса та порт у primary_conninfo.
Чи дозволяє firewall з'єднання зі standby.
Чи є правильний рядок у pg_hba.conf.
Чи доступний пароль у .pgpass.
Чи перечитав primary pg_hba.conf.
Чи не використовується неправильна версія або інший екземпляр PostgreSQL.
Журнал PostgreSQL часто містить точну причину відмови:
sudo journalctl -u postgresql --since "10 minutes ago"pg_stat_replication немає рядківПеревірте на standby:
SELECT * FROM pg_stat_wal_receiver;Якщо результат порожній, WAL-приймач не працює або standby не вважає себе standby. Перевірте:
ls -l /var/lib/postgresql/16/main/standby.signalТакож перевірте:
SELECT pg_is_in_recovery();Якщо результат false, сервер не запущений у режимі standby.
Перевірте:
SELECT
slot_name,
active,
restart_lsn
FROM pg_replication_slots;Якщо active = false, жоден standby зараз не використовує слот. Не видаляйте слот без перевірки: його видалення може призвести до втрати WAL, необхідного для відновлення реплікації.
Можливі причини:
повільний мережевий канал;
високе навантаження на диск standby;
довгі запити або конфлікти читання на standby;
недостатня продуктивність standby для застосування WAL;
затримка через обмеження ресурсів.
Почніть із перевірки pg_stat_replication, pg_stat_wal_receiver і журналів обох серверів.
pg_hba.confПравило:
host replication repl_user 10.0.0.20/32 scram-sha-256дозволяє лише адресу 10.0.0.20.
Маска /24 дозволила б цілу підмережу, тому її варто використовувати лише за обґрунтованої потреби.
pg_basebackup не від імені власника PostgreSQLКаталог даних має належати користувачеві PostgreSQL. Запускайте команду так:
sudo -u postgres pg_basebackup ...або забезпечте правильного власника та права для всіх створених файлів.
pg_basebackup створює повну базову копію. Він не призначений для накладання поверх уже використовуваного каталогу даних.
Перед копіюванням:
зупиніть PostgreSQL на standby;
перевірте, що вказано правильний каталог;
переконайтеся, що каталог порожній або містить лише свідомо видалені дані.
Слот утримує WAL, поки standby його не підтвердить. Відключений standby може поступово заповнити диск primary.
Регулярно перевіряйте:
SELECT
slot_name,
active,
pg_size_pretty(
pg_wal_lsn_diff(
pg_current_wal_lsn(),
restart_lsn
)
) AS retained_wal
FROM pg_replication_slots;Фізичний standby у режимі recovery призначений для відтворення WAL і читання. Запити INSERT, UPDATE, DELETE та DDL-зміни на ньому не виконуються.
Стан реплікації потрібно перевіряти з обох боків:
на primary — pg_stat_replication;
на standby — pg_stat_wal_receiver і pg_is_in_recovery().
Одного запиту на primary недостатньо для повної діагностики.
У цьому уроці ви:
налаштували wal_level, WAL-відправників і реплікаційні слоти;
створили окрему роль із правом REPLICATION;
дозволили standby підключатися через pg_hba.conf;
створили базову копію primary за допомогою pg_basebackup;
налаштували standby через standby.signal;
перевірили стан через pg_stat_replication і pg_stat_wal_receiver;
перевірили отримання змін на standby;
оцінили відставання репліки та стан реплікаційного слота.