postgresql-logical-replication/

Потоковая репликация в 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 на обеих машинах производилась следующим образом:

# переменную окружения также можно вписать в ~/.bash_profile
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 следует прописать что-то вроде:

host all all 10.128.0.0/16 trust

Вместо trust на практике может иметь смысл использовать md5 , scram-sha-256 или любую другую аутентификацию, поддерживаемую PostgreSQL. Стоит отметить, что поддержка scram-sha-256 появилась только в PostgreSQL 10.

После внесения изменений в конфигурацию говорим:

pg_ctl -D $PGINSTALL / data-master reload

… и проверяем, что можем ходить с одной машины на другую:

# здесь -U — имя пользователя, а -d — имя базы
psql -h 10.128.0.16 -U eax -d eax

Если все получилось, значит теперь можно воспользоваться логической репликацией.

Использование логической репликации

На обеих машинах создаем какие-нибудь таблицы:

DROP TABLE IF EXISTS test;
CREATE TABLE test ( k TEXT PRIMARY KEY , v TEXT ) ;

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

На мастере заполняем таблицу и создаем публикацию:

INSERT INTO test VALUES ( ‘k1’ , ‘v1’ ) , ( ‘k2’ , ‘v2’ ) ;
CREATE PUBLICATION allpub FOR ALL TABLES ;

Здесь в публикацию были добавлены все сущесвтующие таблицы, но также можно указать и список конкретных таблиц:

CREATE PUBLICATION allpub FOR TABLE table1 , table2;

Кроме того, можно указать, какие именно изменения следует публиковать:

CREATE PUBLICATION allpub
FOR ALL TABLES WITH ( publish = ‘insert,update’ ) ;

Изменение списка таблиц в публикации происходит так:

ALTER PUBLICATION pubname ADD TABLE t1 , t2;
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;

Если все было сделано правильно, на реплике вы увидите все те же данные, что и на мастере. Кроме того, любые изменения таблицы на мастере будут приводить к соответствующим изменениям на реплике. При желании можно проверить работу логической репликации под нагрузкой, например, так:

INSERT INTO test SELECT ‘kk’ || i , ‘vv’ || i
FROM generate_series ( 1 , 10000 ) AS i;

При создании новых таблиц реплика не будет автоматически их тянуть, даже если публикация была создана как FOR ALL TABLES . Как уже было отмечено, DDL не реплицируется. Для решения проблемы нужно создать на реплике таблицы с такой же схемой, что и на мастере (с поправкой на порядок полей и прочее, см выше), а затем сказать:

ALTER SUBSCRIPTION allsub REFRESH PUBLICATION ;

Подписку можно временно включать и выключать:

ALTER SUBSCRIPTION allsub ENABLE ;
ALTER SUBSCRIPTION allsub DISABLE ;

… а также менять мастера:

ALTER SUBSCRIPTION allsub CONNECTION ‘host=…’ ;

Наконец, рассмотрим удаление публикации и подписки:

— на реплике
DROP SUBSCRIPTION allsub;

— на мастере
DROP PUBLICATION allpub;

Казалось бы, что это все. Однако на практике репликацию нужно еще и как-то мониторить. Давайте выясним, как это делается.

Узнаем состояние логической репликации

Просмотр списка публикаций на мастере:

eax=# dRp
List of publications
Name  | Owner | All tables | Inserts | Updates | Deletes
———+——-+————+———+———+———
allpub | eax   | t          | t       | t       | t
(1 row)

Просмотр списка подписок на реплике:

eax=# dRs
List of subscriptions
Name  | Owner | Enabled | Publication
———+——-+———+————-
allsub | eax   | t       | {allpub}
(1 row)

Состояние логической репликации на мастере:

eax=# select * from pg_stat_replication;
-[ 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

Состояние на реплике:

eax=# select * from pg_stat_subscription;
-[ 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

В безысходных ситуациях что на мастере, что на реплике, можно сказать:

SELECT * FROM pg_stat_activity;

Как видите, все необходимые отладочные ручки в наличии.

Заключение

Логическая репликация довольно проста в использовании и открывает много новых возможностей. Стоит однако учесть, что на момент написания этих строк PostgreSQL 10 еще находился в состоянии беты. Для использовании в серьезном продакшене стоит подождать какое-то время (которое зависит от степени серьезности вашего продакшена), пока эту фичу как следует не обкатают.

Дополнение: Вас также может заинтересовать статья Пример использования logical decoding в PostgreSQL .

EnglishRussianUkrainian