Пошук уроків, статей та іншого контенту
Ознайомитеся з логічною та фізичною реплікацією, їхніми сценаріями використання і ключовими обмеженнями.
Реплікація PostgreSQL — це передавання змін із одного кластера PostgreSQL до іншого. Кластер може бути:
primary — основний сервер, на якому виконуються записи;
standby або replica — сервер-копія, який отримує зміни;
publisher — джерело змін для логічної реплікації;
subscriber — отримувач змін у логічній реплікації.
Реплікацію використовують для:
підвищення доступності бази даних;
перемикання на резервний сервер у разі збою;
розподілу навантаження на читання;
перенесення даних між серверами;
вибіркової синхронізації окремих таблиць;
міграції між версіями PostgreSQL.
У PostgreSQL є два основні підходи:
Фізична реплікація — передавання WAL-записів на рівні всього кластера.
Логічна реплікація — передавання логічних змін окремих таблиць.
PostgreSQL записує зміни до журналу передзапису — WAL, або Write-Ahead Log. У WAL зберігається інформація, необхідна для відновлення сторінок даних.
Під час фізичної реплікації standby-сервер отримує WAL-записи від primary-сервера та відтворює їх локально.
Фізична реплікація копіює весь кластер:
усі бази даних;
таблиці та індекси;
системний каталог;
ролі й інші об’єкти кластера, які зберігаються в ньому.
Вона не працює на рівні окремих SQL-команд. Standby отримує низькорівневі зміни сторінок даних.
Найпоширеніший варіант фізичної реплікації — streaming replication.
Primary-сервер запускає процес walsender, а standby — процес walreceiver. WAL передається через з’єднання PostgreSQL.
Реплікація може бути:
асинхронною — primary не чекає підтвердження від standby;
синхронною — primary підтверджує транзакцію лише після того, як визначені standby отримають WAL.
Асинхронна реплікація має меншу затримку, але під час аварії можна втратити останні ще не передані зміни.
Синхронна реплікація зменшує ризик втрати даних, але збільшує час виконання транзакцій і залежить від доступності standby.
На primary потрібно дозволити створення WAL, підключення реплікаційних процесів і реплікаційні слоти:
-- Ці параметри зазвичай змінюють у postgresql.conf
ALTER SYSTEM SET wal_level = 'replica';
ALTER SYSTEM SET max_wal_senders = 10;
ALTER SYSTEM SET max_replication_slots = 10;
-- Користувач для підключення standby-сервера
CREATE ROLE repl WITH REPLICATION LOGIN PASSWORD 'strong-password';Зміна wal_level потребує перезапуску PostgreSQL. Також потрібно дозволити підключення користувача repl у pg_hba.conf на primary:
# Дозволити реплікацію з мережі standby-сервера
host replication repl 10.0.0.20/32 scram-sha-256Адресу мережі потрібно замінити на фактичну адресу standby-сервера.
Початкову копію кластера можна створити за допомогою pg_basebackup:
# Команда виконується на standby-сервері
pg_basebackup \
--host=10.0.0.10 \
--username=repl \
--pgdata=/var/lib/postgresql/data \
--progress \
--write-recovery-conf \
--wal-method=streamПараметр --write-recovery-conf створює конфігурацію підключення до primary та файл standby.signal. У сучасних версіях PostgreSQL наявність standby.signal означає, що кластер має запускатися як standby.
Після запуску standby-сервера можна перевірити його режим:
-- Запит на 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_is_in_recovery() повертає true, сервер працює в режимі відновлення та отримує WAL від primary.
За замовчуванням standby доступний лише для читання. Це дає змогу спрямувати на нього частину SELECT-запитів.
Однак між отриманням WAL і його відтворенням може існувати затримка. Тому результат читання зі standby може бути старішим за результат на primary.
Перевірити затримку на primary можна так:
SELECT
client_addr,
state,
sync_state,
sent_lsn,
write_lsn,
flush_lsn,
replay_lsn
FROM pg_stat_replication;Типові значення sync_state:
async — асинхронна реплікація;
sync — синхронна реплікація;
quorum — standby бере участь у кворумній синхронній реплікації.
Реплікаційний слот зберігає інформацію про те, до якого WAL-позиційного номера споживач уже отримав дані.
Для фізичного слота його можна створити на primary:
SELECT pg_create_physical_replication_slot('standby_slot');У конфігурації standby слот указують через параметр primary_slot_name.
Перевага слота — primary не видаляє WAL, який ще потрібен standby. Недолік — якщо standby надовго зупинений, WAL може накопичитися та заповнити диск primary.
Тому потрібно контролювати стан слотів:
SELECT
slot_name,
slot_type,
active,
restart_lsn,
pg_size_pretty(
pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)
) AS retained_wal
FROM pg_replication_slots;Логічна реплікація передає не сторінки даних, а логічні зміни:
INSERT;
UPDATE;
DELETE;
деякі операції зі структурою таблиць у межах підтримки конкретної версії.
На primary створюють publication — набір таблиць і подій, які потрібно публікувати. На іншому сервері створюють subscription — підписку на publication.
Логічна реплікація дає змогу:
реплікувати лише вибрані таблиці;
передавати дані між різними major-версіями PostgreSQL;
синхронізувати окремі частини бази;
виконувати міграцію даних із меншим простоєм;
мати різні схеми на publisher і subscriber, якщо вони сумісні з операціями реплікації.
Для логічної реплікації на publisher потрібно встановити:
-- Для логічної реплікації потрібен відповідний рівень WAL
ALTER SYSTEM SET wal_level = 'logical';
ALTER SYSTEM SET max_replication_slots = 10;
ALTER SYSTEM SET max_wal_senders = 10;
CREATE ROLE logical_repl
WITH LOGIN REPLICATION PASSWORD 'strong-password';Після зміни wal_level PostgreSQL потрібно перезапустити.
Нехай на publisher є таблиця замовлень:
CREATE TABLE orders (
id bigint PRIMARY KEY,
customer_id bigint NOT NULL,
total numeric(12, 2) NOT NULL,
status text NOT NULL,
created_at timestamptz NOT NULL DEFAULT now()
);
CREATE PUBLICATION orders_publication
FOR TABLE orders;У цьому прикладі до publication входить лише таблиця orders.
На subscriber таблицю потрібно створити заздалегідь:
CREATE TABLE orders (
id bigint PRIMARY KEY,
customer_id bigint NOT NULL,
total numeric(12, 2) NOT NULL,
status text NOT NULL,
created_at timestamptz NOT NULL
);Після цього на subscriber створюють subscription:
CREATE SUBSCRIPTION orders_subscription
CONNECTION 'host=10.0.0.10 port=5432 dbname=appdb user=logical_repl password=strong-password'
PUBLICATION orders_publication
WITH (
copy_data = true,
create_slot = true,
enabled = true
);Параметр copy_data = true означає, що PostgreSQL спочатку скопіює наявні рядки, а потім почне передавати нові зміни.
Приклад зміни на publisher:
INSERT INTO orders (id, customer_id, total, status)
VALUES (1, 42, 199.99, 'new');
UPDATE orders
SET status = 'paid'
WHERE id = 1;Після обробки subscription відповідні зміни з’являться на subscriber.
Стан subscription можна перевірити так:
SELECT
subname,
pid,
received_lsn,
latest_end_lsn,
latest_end_time,
worker_type
FROM pg_stat_subscription;Publication можна налаштувати не лише на таблиці, а й на типи змін:
CREATE PUBLICATION orders_inserts
FOR TABLE orders
WITH (publish = 'insert');Можливі значення publish:
insert;
update;
delete;
truncate.
Також можна додати таблицю до вже створеної publication:
ALTER PUBLICATION orders_publication
ADD TABLE customers;Або видалити її:
ALTER PUBLICATION orders_publication
DROP TABLE customers;За замовчуванням publication не охоплює нові таблиці, які створюються пізніше. Для автоматичного включення таблиць, що відповідають фільтру, можна використати publication для всіх таблиць:
CREATE PUBLICATION app_publication
FOR ALL TABLES;Такий варіант потрібно застосовувати обережно, оскільки він охоплює весь вміст бази.
Для логічної реплікації операцій UPDATE і DELETE PostgreSQL має визначити рядок, який потрібно змінити або видалити на subscriber.
Найкраще для цього підходить первинний ключ:
CREATE TABLE products (
id bigint PRIMARY KEY,
name text NOT NULL,
price numeric(12, 2) NOT NULL
);Якщо таблиця не має первинного ключа, можна визначити унікальний індекс як ідентифікатор реплікації:
ALTER TABLE products
REPLICA IDENTITY USING INDEX products_unique_code_idx;Індекс має бути унікальним і відповідати вимогам PostgreSQL до replica identity.
Крайній варіант — використовувати всі старі значення рядка:
ALTER TABLE products REPLICA IDENTITY FULL;FULL може створювати значне навантаження, особливо для широких таблиць, тому його не варто використовувати без потреби.
| Характеристика | Фізична реплікація | Логічна реплікація | |---|---|---| | Рівень реплікації | WAL і сторінки даних | Логічні зміни рядків | | Обсяг | Увесь кластер | Вибрані таблиці або весь вміст | | Читання на копії | Так, standby доступний для читання | Так, якщо subscriber використовується для читання | | Вибіркова реплікація | Ні | Так | | Різні major-версії | Зазвичай ні | Так, за сумісності | | Перемикання для високої доступності | Основний сценарій | Не є автоматичним повним failover-рішенням | | Реплікація DDL | Копіюється разом із фізичним станом | Зазвичай потребує окремого застосування | | Реплікація ролей і прав | Так, як частина кластера | Ні | | Реплікація послідовностей | Стан кластера копіюється фізично | Потрібно синхронізувати окремо |
Фізична реплікація підходить для резервного сервера всього кластера, але має обмеження:
standby є копією всього кластера, а не окремих таблиць;
primary і standby мають бути сумісними на рівні major-версії;
standby не може мати незалежні записи;
затримка реплікації може призвести до читання застарілих даних;
втрата WAL через неправильне керування слотами може зупинити реплікацію або заповнити диск;
після failover колишній primary не можна просто підключити назад без узгодження стану кластерів.
Фізична реплікація не замінює резервні копії. Помилкове DELETE на primary також буде відтворене на standby.
Логічна реплікація гнучкіша, але вимагає додаткового контролю:
схема таблиць на subscriber має бути підготовлена заздалегідь;
зміни DDL не синхронізуються повністю автоматично;
ролі, права доступу, функції та розширення не реплікуються як дані таблиць;
sequence не синхронізуються так само, як звичайні рядки;
для UPDATE і DELETE потрібна коректна replica identity;
конфлікти можуть виникнути, якщо змінювати ті самі рядки локально на subscriber;
replication slot утримує WAL, поки subscriber не підтвердить його обробку;
початкове копіювання великих таблиць може створити значне навантаження;
таблиці з зовнішніми ключами та залежностями потрібно переносити в сумісному порядку.
Subscriber зазвичай розглядають як отримувач змін. Якщо дозволити паралельні записи на обох серверах, це вже складніша схема, яка потребує окремого вирішення конфліктів.
Використовуйте фізичну реплікацію, якщо потрібно:
створити резервний сервер для failover;
мати повну копію кластера;
розподілити читання між primary і standby;
мінімізувати розбіжності між серверами.
Використовуйте логічну реплікацію, якщо потрібно:
реплікувати лише кілька таблиць;
передавати дані між сумісними різними major-версіями;
виконати поступову міграцію;
передавати окремі типи змін;
побудувати окремий subscriber для аналітики або інтеграції.
У великих системах ці підходи можуть використовуватися одночасно: фізична реплікація — для високої доступності, а логічна — для вибіркової передачі даних.
wal_levelДля фізичної реплікації потрібен щонайменше wal_level = replica, а для логічної — wal_level = logical.
Після зміни параметра сервер потрібно перезапустити.
pg_hba.confНавіть правильно створений користувач не зможе підключитися, якщо для його адреси немає дозволу в pg_hba.conf.
Після зміни цього файла конфігурацію потрібно перечитати:
SELECT pg_reload_conf();Таблиця без первинного ключа може нормально реплікувати INSERT, але для UPDATE і DELETE виникнуть проблеми з пошуком відповідного рядка.
Зупинений subscriber або standby може утримувати значний обсяг WAL. Потрібно регулярно перевіряти pg_replication_slots і вчасно видаляти непотрібні слоти.
Створення таблиці, індексу або зміна структури на publisher не означає, що така сама операція автоматично буде виконана на subscriber у логічній реплікації.
Структуру потрібно змінювати на subscriber окремо та узгоджено з процесом реплікації.
Реплікація передає помилки й небажані зміни. Для відновлення після логічної помилки, пошкодження даних або видалення таблиці потрібні резервні копії та відповідна стратегія відновлення.
Фізична реплікація передає WAL і створює майже точну копію всього кластера.
Фізична реплікація добре підходить для high availability, failover і read-only standby.
Логічна реплікація передає зміни таблиць через publication і subscription.
Логічна реплікація дає змогу вибирати таблиці та підтримувати сценарії міграції між версіями.
Для UPDATE і DELETE у логічній реплікації потрібен первинний ключ або інша replica identity.
DDL, ролі, права, розширення та послідовності потрібно враховувати окремо.
Реплікаційні слоти потрібно контролювати, оскільки вони можуть утримувати WAL і заповнити диск.
Реплікація доповнює резервне копіювання, але не замінює його.