Пример использования логической репликации PostgreSQL
26 июля 2017
Потоковая репликация в PostgreSQL имеет ряд ограничений. Наиболее существенное среди них, пожалуй, заключается в том, что вы не можете перейти на следующую версию PostgreSQL без полной остановки кластера хотя бы на несколько минут. Поэтому в PostgreSQL 10 была представлена логическая репликация, решающую как эту, так и ряд других проблем.
Возможности и ограничения логической репликации
Итак, зачем же все это нужно:
- Обновление без даунтайма;
- Репликация только части данных, а не всего подряд;
- На реплике можно использовать временные таблицы, да и вообще писать что угодно, в том числе в реплицируемые таблицы;
- Одна реплика может подтягивать данные с двух и более мастеров;
- И прочие сценарии, когда потоковая репликация чем-то не подошла;
Стоит также отметить, что схема таблиц на мастере и на реплике может различаться. Во-первых, может отличаться порядок столбцов. Во-вторых, на реплике таблица может иметь nullable столбцы, которых нет на мастере. Тогда при репликации в них будет записываться NULL. Однако в таблице на мастере не может быть больше столбцов, чем на реплике, даже если в этих столбцах хранятся только NULL’ы. Если такое случится, репликация встанет и возобновится только после решения проблемы. То есть, добавления столбцов на реплике или исправления схемы на мастере.
Важное ограничение логической репликации заключается в том, что реплицируемые таблицы должны иметь primary key. Кроме того, DDL, операция TRUNCATE, а также sequences не реплицируются.
Установка и предварительная настройка PostgreSQL
Далее будет описана упрощенная установка PostgreSQL. Она подходит для тестирования логической репликации, но не для использования в боевом окружении. Если вас интересует что-то более приближенное к использованию в бою, обратите внимание на заметки Начало работы с PostgreSQL, а также PostgreSQL: сборка из исходников и настройка под Linux.
Все описанные ниже шаги были проверены мной на PostgreSQL 10 Beta 2 при помощи двух виртуалок. Для работы с виртуалками я использую самопальный скрипт на Python, ранее описанный в заметке Управление VirtualBox из консоли с помощью vboxmanage. Мастер работал под управлением Arch Linux, а реплика — под управлением Ubuntu. Установка PostgreSQL на обеих машинах производилась следующим образом:
export PGINSTALL=~/postgresql-install
export PATH="$PGINSTALL/bin:$PATH"
mkdir -p $PGINSTALL
git clone https://github.com/afiskon/pgscripts.git
git clone git://git.postgresql.org/git/postgresql.git
cd postgresql
git checkout REL_10_BETA2
./configure --prefix=$PGINSTALL # --enable-cassert --enable-debug
make && make check
~/pgscripts/single-install.sh
Скрипт single-install.sh из моей коллекции pgscripts автоматически настраивает PostgreSQL пригодным для использования логической репликации образом. Однако в бою вы скорее всего не сможете на него положится. Поэтому рассмотрим параметры из postgresql.conf, на которые стоит обратить внимание.
На мастере:
- wal_level — должен быть как минимум logical;
- listen_address — сервер должен слушать интерфейс, через который в него будет ходить реплика;
- max_wal_senders — ожидаемое число реплик, плюс небольшой запас для случаев, когда реплика переподключается, а старое соединение еще не закрылось по таймауту;
- max_connections — должен быть равен max_wal_senders прибавить ожидаемое число клиентов;
- max_replication_slots — максимальное количество replication slot’ов;
- synchronous_commit — имеет такой же смысл для логической репликации, что и для потоковой, то есть, стоит поставить в
on
илиremote_apply
;
На реплике, скорее всего, ничего тюнить не придется, но на всякий случай стоит перепроверить:
- max_worker_processes;
- max_replication_slots;
- max_sync_workers_per_subscription;
- max_logical_replication_workers;
Полное и самое подробное описание всех упомянутых параметров вы найдете в официальной документации.
У меня машины объединены в сеть 10.128.0.0/16. Чтобы можно было ходить с одной машины на другую, в pg_hba.conf следует прописать что-то вроде:
Вместо trust
на практике может иметь смысл использовать md5
, scram-sha-256
или любую другую аутентификацию, поддерживаемую PostgreSQL. Стоит отметить, что поддержка scram-sha-256
появилась только в PostgreSQL 10.
После внесения изменений в конфигурацию говорим:
… и проверяем, что можем ходить с одной машины на другую:
psql -h 10.128.0.16 -U eax -d eax
Если все получилось, значит теперь можно воспользоваться логической репликацией.
Использование логической репликации
На обеих машинах создаем какие-нибудь таблицы:
CREATE TABLE test(k TEXT PRIMARY KEY, v TEXT);
Важно! Напомню, что у таблицы обязательно должен быть первичный ключ, без этого логическая репликация не заработает.
На мастере заполняем таблицу и создаем публикацию:
CREATE PUBLICATION allpub FOR ALL TABLES;
Здесь в публикацию были добавлены все сущесвтующие таблицы, но также можно указать и список конкретных таблиц:
Кроме того, можно указать, какие именно изменения следует публиковать:
FOR ALL TABLES WITH (publish = 'insert,update');
Изменение списка таблиц в публикации происходит так:
ALTER PUBLICATION pubname SET TABLE t3, t4;
ALTER PUBLICATION pubname DROP TABLE t5, t6;
На реплике создаем подписку и проверяем содержимое таблицы:
CREATE SUBSCRIPTION allsub
CONNECTION 'host=10.128.0.16 user=eax dbname=eax'
PUBLICATION allpub;
SELECT * FROM test;
Если все было сделано правильно, на реплике вы увидите все те же данные, что и на мастере. Кроме того, любые изменения таблицы на мастере будут приводить к соответствующим изменениям на реплике. При желании можно проверить работу логической репликации под нагрузкой, например, так:
FROM generate_series(1, 10000) AS i;
При создании новых таблиц реплика не будет автоматически их тянуть, даже если публикация была создана как FOR ALL TABLES
. Как уже было отмечено, DDL не реплицируется. Для решения проблемы нужно создать на реплике таблицы с такой же схемой, что и на мастере (с поправкой на порядок полей и прочее, см выше), а затем сказать:
Подписку можно временно включать и выключать:
ALTER SUBSCRIPTION allsub DISABLE;
… а также менять мастера:
Наконец, рассмотрим удаление публикации и подписки:
DROP SUBSCRIPTION allsub;
-- на мастере
DROP PUBLICATION allpub;
Казалось бы, что это все. Однако на практике репликацию нужно еще и как-то мониторить. Давайте выясним, как это делается.
Узнаем состояние логической репликации
Просмотр списка публикаций на мастере:
List of publications
Name | Owner | All tables | Inserts | Updates | Deletes
--------+-------+------------+---------+---------+---------
allpub | eax | t | t | t | t
(1 row)
Просмотр списка подписок на реплике:
List of subscriptions
Name | Owner | Enabled | Publication
--------+-------+---------+-------------
allsub | eax | t | {allpub}
(1 row)
Состояние логической репликации на мастере:
-[ RECORD 1 ]----+------------------------------
pid | 24649
usesysid | 10
usename | eax
application_name | allsub
client_addr | 10.128.0.23
client_hostname |
client_port | 41736
backend_start | 2017-07-17 19:19:57.617374+03
backend_xmin |
state | streaming
sent_lsn | 0/163C100
write_lsn | 0/163C100
flush_lsn | 0/163C100
replay_lsn | 0/163C100
write_lag |
flush_lag |
replay_lag |
sync_priority | 0
sync_state | async
eax=# select * from pg_replication_slots;
-[ RECORD 1 ]-------+----------
slot_name | allsub
plugin | pgoutput
slot_type | logical
datoid | 16384
database | eax
temporary | f
active | t
active_pid | 24723
xmin |
catalog_xmin | 582
restart_lsn | 0/1696600
confirmed_flush_lsn | 0/1696638
Состояние на реплике:
-[ RECORD 1 ]---------+------------------------------
subid | 16408
subname | allsub
pid | 2411
relid |
received_lsn | 0/163C100
last_msg_send_time | 2017-07-17 19:20:31.400501+03
last_msg_receipt_time | 2017-07-17 16:25:09.14483+03
latest_end_lsn | 0/163C100
latest_end_time | 2017-07-17 19:20:31.400501+03
В безысходных ситуациях что на мастере, что на реплике, можно сказать:
Как видите, все необходимые отладочные ручки в наличии.
Заключение
Логическая репликация довольно проста в использовании и открывает много новых возможностей. Стоит однако учесть, что на момент написания этих строк PostgreSQL 10 еще находился в состоянии беты. Для использовании в серьезном продакшене стоит подождать какое-то время (которое зависит от степени серьезности вашего продакшена), пока эту фичу как следует не обкатают.
Дополнение: Вас также может заинтересовать статья Пример использования logical decoding в PostgreSQL.
Метки: PostgreSQL, СУБД.
Вы можете прислать свой комментарий мне на почту, или воспользоваться комментариями в Telegram-группе.