サブスクリプションは論理レプリケーションの下流側です。 サブスクリプションが定義されたノードはサブスクライバーとして参照されます。 サブスクリプションは他のデータベースへの接続と、サブスクリプション対象の一つ以上のパブリケーションの集合を定義します。
サブスクライバーのデータベースは、他のPostgreSQLインスタンスと同様に振る舞い、自分用のパブリケーションを定義することにより、他のデータベースに対するパブリッシャーとして利用できます。
サブスクライバーノードは、必要ならば複数のサブスクリプションを持つことができます。 一組のパブリッシャーとサブスクライバーの間で複数のサブスクリプションを定義することもできますが、サブスクライブしたパブリケーションオブジェクトが重複しないように注意が必要です。
各々のサブスクリプションは、一つのレプリケーションスロット(27.2.6を参照)を通じて更新が通知されます。 既存のテーブルデータを初期同期するために、追加でレプリケーションスロットが必要になることもあります。それらはデータ同期の終了時に削除されます。
論理レプリケーションのサブスクリプションは、同期レプリケーション(27.2.8参照)のスタンバイであっても構いません。
スタンバイ名称はデフォルトではサブスクリプション名となります。
サブスクリプションのコネクション情報の中のapplication_name
を別の名前として指定することもできます。
現在のユーザがスーパーユーザならば、サブスクリプションはpg_dump
でダンプできます。
そうでない場合には、警告が出力され、サブスクリプションはスキップされます。
非スーパーユーザはすべてのサブスクリプション情報を、pg_subscription
カタログから読み出せないからです。
サブスクリプションはCREATE SUBSCRIPTION
で追加し、ALTER SUBSCRIPTION
を使って、いつでも停止、再開でき、そしてDROP SUBSCRIPTION
で削除できます。
サブスクリプションが削除され、そして再作成されると、同期情報は失われます。 このことは、後でデータを再同期しなければならないことを意味します。
スキーマ定義情報はレプリケーションされないので、パブリッシュするテーブルはサブスクライバーに存在しなければなりません。 通常のテーブルだけがレプリケーションの対象です。 たとえば、ビューはレプリケーションできません。
パブリッシャーとサブスクライバーの間でのテーブルの照合は、完全修飾されたテーブル名に基づいて行われます。 サブスクライバーで異なる名前になっているテーブルに対するレプリケーションは、サポートされていません。
テーブルの列も名前で照合されます。
サブスクライバーのテーブルでの列の順序はパブリッシャーと一致している必要はありません。
データのテキスト表現列が対象の型に変換可能である限り、列のデータ型も一致している必要がありません。
例えば、integer
型の列からbigint
型の列にレプリケーションすることができます。
対象テーブルはパブリッシュされたテーブルにない追加の列を持つこともできます。
そうした列には対象テーブルの定義の指定に従ってデフォルト値が挿入されます。
しかしながら、バイナリ形式の論理レプリケーションはより制限的です。
詳細は、CREATE SUBSCRIPTION
のbinary
オプションを参照してください。
前述のように、各々の(有効な)サブスクリプションは、リモート(パブリッシュしている)側のレプリケーションスロットに対する変更を受信します。
追加的なテーブル同期スロットは通常一時的なもので、初期テーブル同期を実行するために内部的に作成され、不要になった時に自動的に削除されます。
これらのテーブル同期スロットには「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
では、一部のパブリッシュ
操作が許可されません。
パブリケーション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
は、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)
通常のレプリケーションでは、適切なパブリッシュ
操作が使用されていることに注意してください。
これは、パブリケーション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)
リモートレプリケーションスロットが自動的に作成されない場合(例えば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 = 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