Перейти к основному содержимому

Подписка

примечание

Эта страница переведена при помощи нейросети GigaChat.

Подписка – это принимающая сторона логической репликации. Узел, на котором определена подписка, называется подписчиком. Подписка определяет соединение с другой базой данных и набор публикаций (один или несколько), к которым она хочет подключиться.

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

Если необходимо, у узла-подписчика может быть несколько подписок. Возможно определить несколько подписок между одной парой издатель-подписчик, в этом случае следует позаботиться о том, чтобы объекты подписанных публикаций не перекрывались.

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

Подписка логической репликации может быть резервной для синхронной репликации. Имя ведомого узла по умолчанию – это имя подписки. Альтернативное имя можно указать как application_name в информации о подключении подписки.

Подписки выгружаются с помощью pg_dump если текущий пользователь является суперпользователем. В противном случае выдается предупреждение и подписки пропускаются, потому что несуперпользователи не могут прочитать всю информацию о подписках из каталога pg_subscription.

Подписка добавляется с использованием CREATE SUBSCRIPTION и может быть остановлена/возобновлена в любое время с помощью команды ALTER SUBSCRIPTION и удалена с использованием DROP SUBSCRIPTION.

Когда подписка отменяется и воссоздается, информация о синхронизации теряется. Это означает, что данные должны быть повторно синхронизированы впоследствии.

Определения схемы не реплицируются, а опубликованные таблицы должны существовать у подписчика. Только обычные таблицы могут быть целью репликации. Например, нельзя реплицировать представление.

Таблицы сопоставляются между издателем и подписчиком с использованием полностью квалифицированного имени таблицы. Репликация в таблицы с разными именами на стороне подписчика не поддерживается.

Столбцы таблицы также сопоставляются по имени. Порядок столбцов в таблице подписчика не обязательно должен соответствовать порядку издателя. Типы данных столбцов не должны совпадать, если текстовое представление данных может быть преобразовано в целевой тип. Например, можно реплицировать данные из столбца типа integer в столбец типа bigint. Целевая таблица также может содержать дополнительные столбцы, не предоставляемые опубликованной таблицей. Любые такие столбцы будут заполнены значением по умолчанию, указанным в определении целевой таблицы.

Логическая репликация в двоичном формате имеет более строгие ограничения. За подробностями обратитесь к описанию параметра binary команды CREATE SUBSCRIPTION.

Управление слотами репликации

Как упоминалось ранее, каждая (активная) подписка получает изменения из слота репликации на удаленной (издающей) стороне.

Дополнительные слоты синхронизации таблиц обычно являются временными и создаются автоматически для выполнения начальной синхронизации таблиц и автоматически удаляются, когда они больше не нужны. Эти слоты синхронизации таблиц имеют сгенерированные имена: «pg_%u_sync_%u_%llu» (параметры: Подписка oid, Таблица relid, идентификатор системы sysid)

Обычно удаленный слот репликации создается автоматически при создании подписки с использованием CREATE SUBSCRIPTION и удаляется автоматически при удалении подписки с использованием DROP SUBSCRIPTION. Однако в некоторых ситуациях может быть полезно или необходимо манипулировать подпиской и лежащим в ее основе слотом репликации отдельно. Вот несколько сценариев:

  • При создании подписки слот репликации уже существует. В этом случае подписка может быть создана с использованием опции create_slot = false для ассоциации с существующим слотом.
  • При создании подписки удаленный хост недоступен или находится в неопределенном состоянии. В этом случае подписку можно создать с помощью параметра connect = false. Удаленный хост тогда вообще не будет контактировать. Это то, что использует pg_dump. Затем удаленный слот репликации должен быть создан вручную, прежде чем подписка сможет быть активирована.
  • При отмене подписки слот репликации следует сохранить. Это может быть полезно, когда база данных подписчика перемещается на другой хост и будет активирована оттуда. В этом случае отключите слот от подписки с помощью ALTER SUBSCRIPTION перед попыткой отменить подписку.
  • При удалении подписки удаленный хост недоступен. В этом случае отключите слот от подписки с помощью ALTER SUBSCRIPTION перед попыткой удалить подписку. Если экземпляр удаленной базы данных больше не существует, дополнительных действий не требуется. Однако если экземпляр удаленной базы данных просто недоступен, то слот репликации (и все оставшиеся слоты синхронизации таблиц) следует затем удалить вручную, в противном случае он/они будут продолжать резервировать WAL и могут в конечном итоге привести к заполнению диска. Такие случаи должны быть тщательно изучены.

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

Создайте несколько тестовых таблиц на издателе:

test_pub=# CREATE TABLE t1(a int, b text, PRIMARY KEY(a));
CREATE TABLE
test_pub=# CREATE TABLE t2(c int, d text, PRIMARY KEY(c));
CREATE TABLE
test_pub=# CREATE TABLE t3(e int, f text, PRIMARY KEY(e));
CREATE TABLE

Создайте те же таблицы на подписчике:

test_sub=# CREATE TABLE t1(a int, b text, PRIMARY KEY(a));
CREATE TABLE
test_sub=# CREATE TABLE t2(c int, d text, PRIMARY KEY(c));
CREATE TABLE
test_sub=# CREATE TABLE t3(e int, f text, PRIMARY KEY(e));
CREATE TABLE

Вставьте данные в таблицы на стороне издателя:

test_pub=# INSERT INTO t1 VALUES (1, 'one'), (2, 'two'), (3, 'three');
INSERT 0 3
test_pub=# INSERT INTO t2 VALUES (1, 'A'), (2, 'B'), (3, 'C');
INSERT 0 3
test_pub=# INSERT INTO t3 VALUES (1, 'i'), (2, 'ii'), (3, 'iii');
INSERT 0 3

Создайте публикации для таблиц. Публикации pub2 и pub3a запрещают некоторые операции publish. В публикации pub3b есть фильтр строки (см. раздел «Фильтры строк»):

test_pub=# CREATE PUBLICATION pub1 FOR TABLE t1;
CREATE PUBLICATION
test_pub=# CREATE PUBLICATION pub2 FOR TABLE t2 WITH (publish = 'truncate');
CREATE PUBLICATION
test_pub=# CREATE PUBLICATION pub3a FOR TABLE t3 WITH (publish = 'truncate');
CREATE PUBLICATION
test_pub=# CREATE PUBLICATION pub3b FOR TABLE t3 WHERE (e > 5);
CREATE PUBLICATION

Создайте подписки на публикации. Подписка sub3 подписывается как на pub3a, так и на pub3b. Все подписки будут копировать исходные данные по умолчанию:

test_sub=# CREATE SUBSCRIPTION sub1
test_sub-# CONNECTION 'host=localhost dbname=test_pub application_name=sub1'
test_sub-# PUBLICATION pub1;
CREATE SUBSCRIPTION
test_sub=# CREATE SUBSCRIPTION sub2
test_sub-# CONNECTION 'host=localhost dbname=test_pub application_name=sub2'
test_sub-# PUBLICATION pub2;
CREATE SUBSCRIPTION
test_sub=# CREATE SUBSCRIPTION sub3
test_sub-# CONNECTION 'host=localhost dbname=test_pub application_name=sub3'
test_sub-# PUBLICATION pub3a, pub3b;
CREATE SUBSCRIPTION

Обратите внимание, что начальные данные таблицы копируются независимо от операции публикации:

test_sub=# SELECT * FROM t1;
a | b
---+-------
1 | one
2 | two
3 | three
(3 rows)

test_sub=# SELECT * FROM t2;
c | d
---+---
1 | A
2 | B
3 | C
(3 rows)

Кроме того, поскольку исходная копия данных игнорирует операцию публикации и поскольку публикация pub3a не имеет фильтра строк, это означает, что скопированная таблица t3 содержит все строки, даже если они не соответствуют фильтру строк публикации pub3b:

test_sub=# SELECT * FROM t3;
e | f
---+-----
1 | i
2 | ii
3 | iii
(3 rows)

Вставьте больше данных в таблицы на стороне издателя:

test_pub=# INSERT INTO t1 VALUES (4, 'four'), (5, 'five'), (6, 'six');
INSERT 0 3
test_pub=# INSERT INTO t2 VALUES (4, 'D'), (5, 'E'), (6, 'F');
INSERT 0 3
test_pub=# INSERT INTO t3 VALUES (4, 'iv'), (5, 'v'), (6, 'vi');
INSERT 0 3

Теперь данные на стороне издателя выглядят так:

test_pub=# SELECT * FROM t1;
a | b
---+-------
1 | one
2 | two
3 | three
4 | four
5 | five
6 | six
(6 rows)

test_pub=# SELECT * FROM t2;
c | d
---+---
1 | A
2 | B
3 | C
4 | D
5 | E
6 | F
(6 rows)

test_pub=# SELECT * FROM t3;
e | f
---+-----
1 | i
2 | ii
3 | iii
4 | iv
5 | v
6 | vi
(6 rows)

Обратите внимание, что во время нормальной репликации используются соответствующие publish операции. Это означает, что публикации pub2 и pub3a не будут реплицировать INSERT. Также публикация pub3b будет реплицировать только те данные, которые соответствуют фильтру строки pub3b. Теперь данные на стороне подписчика выглядят следующим образом:

test_sub=# SELECT * FROM t1;
a | b
---+-------
1 | one
2 | two
3 | three
4 | four
5 | five
6 | six
(6 rows)

test_sub=# SELECT * FROM t2;
c | d
---+---
1 | A
2 | B
3 | C
(3 rows)

test_sub=# SELECT * FROM t3;
e | f
---+-----
1 | i
2 | ii
3 | iii
6 | vi
(4 rows)

Примеры: Отложенное создание слота репликации

Существуют случаи (например, см. раздел Раздел 29.2.1), когда если удаленный слот репликации не был создан автоматически, пользователь должен создать его вручную перед активацией подписки. Шаги по созданию слота и активации подписки показаны в следующих примерах. Эти примеры указывают стандартный плагин вывода логического декодирования (pgoutput), который используется встроенной логической репликацией.

Сначала создайте публикацию для использования в примерах.

test_pub=# CREATE PUBLICATION pub1 FOR ALL TABLES;
CREATE PUBLICATION

Пример 1: Где подписка говорит connect = false

  • Создать подписку.

    test_sub=# CREATE SUBSCRIPTION sub1
    test_sub-# CONNECTION 'host=localhost dbname=test_pub'
    test_sub-# PUBLICATION pub1
    test_sub-# WITH (connect=false);
    WARNING: subscription was created, but is not connected
    HINT: To initiate replication, you must manually create the replication slot, enable the subscription, and refresh the subscription.
    CREATE SUBSCRIPTION
  • На сервере-издателе вручную создайте слот. Поскольку имя не было указано во время выполнения команды CREATE SUBSCRIPTION, имя создаваемого слота совпадает с именем подписки, например "sub1".

    test_pub=# SELECT * FROM pg_create_logical_replication_slot('sub1', 'pgoutput');
    slot_name | lsn
    -----------+-----------
    sub1 | 0/19404D0
    (1 row)
  • На сервере-подписчике завершите активацию подписки. После этого начнется репликация таблиц pub1.

    test_sub=# ALTER SUBSCRIPTION sub1 ENABLE;
    ALTER SUBSCRIPTION
    test_sub=# ALTER SUBSCRIPTION sub1 REFRESH PUBLICATION;
    ALTER SUBSCRIPTION

Пример 2: Где подписка говорит connect = false, но также указывает опцию slot_name.

  • Создайте подписку.

    test_sub=# CREATE SUBSCRIPTION sub1
    test_sub-# CONNECTION 'host=localhost dbname=test_pub'
    test_sub-# PUBLICATION pub1
    test_sub-# WITH (connect=false, slot_name='myslot');
    WARNING: subscription was created, but is not connected
    HINT: To initiate replication, you must manually create the replication slot, enable the subscription, and refresh the subscription.
    CREATE SUBSCRIPTION
  • На сервере-издателе вручную создайте слот с тем же именем, которое было указано во время создания подписки, например "myslot".

    test_pub=# SELECT * FROM pg_create_logical_replication_slot('myslot', 'pgoutput');
    slot_name | lsn
    -----------+-----------
    myslot | 0/19059A0
    (1 row)
  • На сервере-подписчике оставшиеся шаги активации подписки аналогичны предыдущим.

    test_sub=# ALTER SUBSCRIPTION sub1 ENABLE;
    ALTER SUBSCRIPTION
    test_sub=# ALTER SUBSCRIPTION sub1 REFRESH PUBLICATION;
    ALTER SUBSCRIPTION

Пример 3: Когда подписка указывает slot_name = NONE

  • Создайте подписку. При указании slot_name = NONE необходимы также параметры enabled = false и create_slot = false.

    test_sub=# CREATE SUBSCRIPTION sub1
    test_sub-# CONNECTION 'host=localhost dbname=test_pub'
    test_sub-# PUBLICATION pub1
    test_sub-# WITH (slot_name=NONE, enabled=false, create_slot=false);
    CREATE SUBSCRIPTION
  • На сервере-издателе вручную создайте слот с любым именем, например "myslot".

    test_pub=# SELECT * FROM pg_create_logical_replication_slot('myslot', 'pgoutput');
    slot_name | lsn
    -----------+-----------
    myslot | 0/1905930
    (1 row)
  • На сервере-подписчике свяжите подписку с только что созданным именем слота.

    test_sub=# ALTER SUBSCRIPTION sub1 SET (slot_name='myslot');
    ALTER SUBSCRIPTION
  • Оставшиеся шаги активации подписки остаются такими же, как прежде.

    test_sub=# ALTER SUBSCRIPTION sub1 ENABLE;
    ALTER SUBSCRIPTION
    test_sub=# ALTER SUBSCRIPTION sub1 REFRESH PUBLICATION;
    ALTER SUBSCRIPTION