他のバージョンの文書 16 | 15 | 14 | 13 | 12 | 11 | 10 | 9.6 | 9.5 | 9.4 | 9.3 | 9.2 | 9.1 | 9.0 | 8.4 | 8.3 | 8.2 | 8.1 | 8.0 | 7.4 | 7.3 | 7.2

31.2. サブスクリプション #

サブスクリプションは論理レプリケーションの下流側です。 サブスクリプションが定義されたノードはサブスクライバーとして参照されます。 サブスクリプションは他のデータベースへの接続と、サブスクリプション対象の一つ以上のパブリケーションの集合を定義します。

サブスクライバーのデータベースは、他のPostgreSQLインスタンスと同様に振る舞い、自分用のパブリケーションを定義することにより、他のデータベースに対するパブリッシャーとして利用できます。

サブスクライバーノードは、必要ならば複数のサブスクリプションを持つことができます。 一組のパブリッシャーとサブスクライバーの間で複数のサブスクリプションを定義することもできますが、サブスクライブしたパブリケーションオブジェクトが重複しないように注意が必要です。

各々のサブスクリプションは、一つのレプリケーションスロット(27.2.6を参照)を通じて更新が通知されます。 既存のテーブルデータを初期同期するために、追加でレプリケーションスロットが必要になることもあります。それらはデータ同期の終了時に削除されます。

論理レプリケーションのサブスクリプションは、同期レプリケーション(27.2.8参照)のスタンバイであっても構いません。 スタンバイ名称はデフォルトではサブスクリプション名となります。 サブスクリプションのコネクション情報の中のapplication_nameを別の名前として指定することもできます。

現在のユーザがスーパーユーザならば、サブスクリプションはpg_dumpでダンプできます。 そうでない場合には、警告が出力され、サブスクリプションはスキップされます。 非スーパーユーザはすべてのサブスクリプション情報を、pg_subscriptionカタログから読み出せないからです。

サブスクリプションはCREATE SUBSCRIPTIONで追加し、ALTER SUBSCRIPTIONを使って、いつでも停止、再開でき、そしてDROP SUBSCRIPTIONで削除できます。

サブスクリプションが削除され、そして再作成されると、同期情報は失われます。 このことは、後でデータを再同期しなければならないことを意味します。

スキーマ定義情報はレプリケーションされないので、パブリッシュするテーブルはサブスクライバーに存在しなければなりません。 通常のテーブルだけがレプリケーションの対象です。 たとえば、ビューはレプリケーションできません。

パブリッシャーとサブスクライバーの間でのテーブルの照合は、完全修飾されたテーブル名に基づいて行われます。 サブスクライバーで異なる名前になっているテーブルに対するレプリケーションは、サポートされていません。

テーブルの列も名前で照合されます。 サブスクライバーのテーブルでの列の順序はパブリッシャーと一致している必要はありません。 データのテキスト表現列が対象の型に変換可能である限り、列のデータ型も一致している必要がありません。 例えば、integer型の列からbigint型の列にレプリケーションすることができます。 対象テーブルはパブリッシュされたテーブルにない追加の列を持つこともできます。 そうした列には対象テーブルの定義の指定に従ってデフォルト値が挿入されます。 しかしながら、バイナリ形式の論理レプリケーションはより制限的です。 詳細は、CREATE SUBSCRIPTIONbinaryオプションを参照してください。

31.2.1. レプリケーションスロットの管理 #

前述のように、各々の(有効な)サブスクリプションは、リモート(パブリッシュしている)側のレプリケーションスロットに対する変更を受信します。

追加的なテーブル同期スロットは通常一時的なもので、初期テーブル同期を実行するために内部的に作成され、不要になった時に自動的に削除されます。 これらのテーブル同期スロットにはpg_%u_sync_%u_%llu(パラメータは、サブスクリプションoid、テーブルrelid、システム識別子sysid)という名前が生成されます。

通常、リモートのレプリケーションスロットはCREATE SUBSCRIPTIONでサブスクリプションが作成されるときに自動的に作成され、サブスクリプションがDROP SUBSCRIPTIONで削除されるときに自動的に削除されます。 しかしながら、状況によっては、サブスクリプションとその下にあるレプリケーションスロットを別々に操作することが有用であったり必要であったりします。 以下にいくつかシナリオを示します。

  • サブスクリプションを作る際、レプリケーションスロットがすでに存在しています。 この場合、create_slot = falseオプションを使ってサブスクリプションを作成し、既存のスロットと関連付けることができます。

  • サブスクリプションを作成する際に、リモートホストが接続できない状態にあるか、不明な状況にあります。 こうした時は、connect = falseを使ってサブスクリプションを作成することができます。 リモートホストにはまったく接続しません。 これは、pg_dumpが使っている方法です。 サブスクリプションを有効にする前に、リモートホストのレプリケーションスロットを手動で作成しなければなりません。

  • サブスクリプションを削除する際に、レプリケーションスロットを維持する必要があります。 サブスクライバーのデータベースが別のホストに移動中で、移動後にそこからデータベースを起動するときに有効です。 この場合、サブスクリプションを削除する前に、ALTER SUBSCRIPTIONでそのスロットを切り離します。

  • サブスクリプションを削除する際に、リモートホストに接続できません。 この場合、サブスクリプションを削除する前に、ALTER SUBSCRIPTIONでそのスロットを切り離しを試みます。 リモートデータベースインスタンスが存在しない場合は、これ以上の操作は必要ありません。 しかし、単にリモートデータベースに接続できない状態ならば、レプリケーションスロット(および、未だ残っている全てのテーブル同期スロット)を手動で削除する必要があります。 そうでなければ、WALが保存され続け、いずれディスクを埋め尽くすかもしれません。 そのような状態は注意深く調査する必要があります。

31.2.2. 例: 論理レプリケーションの設定 #

パブリッシャーでいくつかテストテーブルを作成します。

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

テーブルのパブリケーションを作成します。 パブリケーションpub2pub3aでは、一部のパブリッシュ操作が許可されません。 パブリケーションpub3bには行フィルタがあります(31.3を参照)。

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は、pub3apub3bの両方をサブスクライブします。 デフォルトでは、すべてのサブスクリプションによって初期データがコピーされます。

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)

通常のレプリケーションでは、適切なパブリッシュ操作が使用されていることに注意してください。 これは、パブリケーションpub2およびpub3aINSERTをレプリケーションしないことを意味します。 また、パブリケーションpub3bpub3bの行フィルタに一致するデータのみをレプリケーションします。 サブスクライバー側のデータは次のようになります:

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)

31.2.3. 例: レプリケーションスロットの遅延作成 #

リモートレプリケーションスロットが自動的に作成されない場合(例えば31.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
    
  • パブリッシャーで、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 = falsecreate_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