originパラメーターによる双方向の論理レプリケーション - PostgreSQL 16でコミットされた機能の紹介:技術者Blog
PostgreSQLインサイド

Vigneshwaran C

FUJITSU Limited
Software Open Innovation Business Unit Data Management Division
Software Lead Developer

はじめに

このブログでは、PostgreSQL 16の新機能である、originパラメーターによるフィルタリングを使用した双方向の論理レプリケーションについて簡単に紹介します。

背景

通常、論理レプリケーションのパブリッシュとサブスクライブモデルを使用する場合、パブリッシュされたテーブルのすべての更新データは、データのorigin(起点)に関係なく、適切なサブスクライバーにレプリケートされます。論理レプリケーションのデータをそのoriginでフィルタリングする新しい機能により、そのパブリッシャーで発生した更新データのみがレプリケートされるように、レプリケーションを制限できるようになりました。これは、レプリケーションのプライマリーノード間での再帰(同じデータの無限レプリケーション)を回避するために使用できます。この機能の重要な点は、WALレコードにデータのoriginが含まれていることです。これは、パブリッシャーがそれに従ってパブリッシュするのに役立ちます。

目次

機能の概要

PostgreSQLのCREATE SUBSCRIPTION文に新しいパラメーター「origin」 が追加されました。これは、サブスクリプションがパブリッシャーに対して、originに関連付けられていない更新データのみを送るか、あるいはoriginの関連付けに関係なく更新データを送るかを指定します。

originにNONEを設定すると、サブスクリプションはoriginを持たない更新データのみを送信するようにパブリッシャーに要求します。
originにANYを設定すると、パブリッシャーはoriginに関係なく更新データを送信します。デフォルトはANYです。

図1:originパラメーターによるフィルタリングの概要
図1:originパラメーターによるフィルタリングの概要

  • 注意)

    上記origin=NONEの例では、ノード2は「node1_data」行のデータをノード3にレプリケートしません。このデータはノード1からのものであるためです。

CRATE SUBSCRIPTION構文の改良

次に、簡略化したCREATE/ALTER SUBSCRIPTION構文を示します。PostgreSQL 16で追加された新しいoriginパラメーターを強調表示しています。

CREATE SUBSCRIPTION <sub-name> CONNECTION 'conninfo' PUBLICATION <pub-list> [WITH (origin = NONE|ANY)]

ALTER SUBSCRIPTION <sub-name> SET (origin = NONE|ANY)

例1

node1=# CREATE SUBSCRIPTION sub_host1_local_data
node1-#   CONNECTION 'host=192.168.1.50 port=5432 user=dba1 dbname=salesdb'
node1-#   PUBLICATION pub_host1 WITH (origin=NONE);

例2

node2=# CREATE SUBSCRIPTION sub_host1_all_data
node2-#    CONNECTION 'host=192.168.1.50 port=5432 user=dba1 dbname=salesdb'
node2-#    PUBLICATION pub_host1 WITH (origin=ANY);

origin=NONEの場合のフィルタリング

パブリッシャー上のwalsenderプロセスはWALファイルをレコード単位で読み取り、デコードします。originパラメーターにNONEが設定されている場合、WALレコードのデコード中、出力プラグインはoriginが関連付けられているすべてのデータを除外します。

図2:origin=NONEでフィルタリングされたトランザクション
図2:origin=NONEでフィルタリングされたトランザクション

プライマリーノード間のレプリケーション

プライマリーノード間のレプリケーションは、いずれのプライマリーノードによって実行された書込み操作もレプリケートするため、マルチマスターデータベース環境を構築するのに便利です。さまざまなシナリオでプライマリーノード間のレプリケーションを作成する手順を次に示します。

2つのプライマリーノード間のレプリケーション設定

次の手順では、両方のプライマリーノードにテーブルデータが存在しないときに、2つのプライマリーノード(primary1とprimary2)間でレプリケーションを設定する方法を示します。

  • 注意)

    セットアップが完了するまで、ノードprimary1およびノードprimary2のテーブルt1に対して、いずれの操作も実行されないようにしてください。

図3:2つのプライマリーノード間の双方向レプリケーション
図3:2つのプライマリーノード間の双方向レプリケーション

  1. ノードprimary1にパブリケーションを作成します。

primary1=# CREATE PUBLICATION pub_pri1 FOR TABLE t1;
CREATE PUBLICATION
  1. ノードprimary2にパブリケーションを作成します。

primary2=# CREATE PUBLICATION pub_pri2 FOR TABLE t1;
CREATE PUBLICATION
  1. primary2からprimary1へのサブスクリプションを作成します。

primary2=# CREATE SUBSCRIPTION sub_pri2_pri1
primary2-# CONNECTION 'dbname=foo host=primary1 user=repuser'
primary2-# PUBLICATION pub_pri1 WITH (origin = NONE);
CREATE SUBSCRIPTION
  1. primary1からprimary2へのサブスクリプションを作成します。

primary1=# CREATE SUBSCRIPTION sub_pri1_pri2
primary1-# CONNECTION 'dbname=foo host=primary2 user=repuser'
primary1-# PUBLICATION pub_pri2 WITH (origin = NONE);
CREATE SUBSCRIPTION

この段階で、primary1とprimary2のノード間のレプリケーションのセットアップは完了です。primary1からの増分の更新データはprimary2にレプリケートされ、その逆も同様です。
以降の手順を実施する前に、以下のSQL文を実行し、ここまでのセットアップが完了しているかを確認します。まず、ノードprimary1のテーブルにデータを挿入します。

primary1=# INSERT INTO t1 VALUES('node1_data1');
INSERT 0 1

次に、ノードprimary2のテーブルを読み取ります。挿入されたデータはレプリケートされます。

primary2=# SELECT * FROM t1;
     c1     
-------------
 node1_data1
(1 row)

では、同じテストをprimary2からprimary1の逆方向に実行します。primary2にデータを挿入してみましょう。テーブルには、上記のprimary1からレプリケートされたデータがすでに含まれていることに注意してください。

primary2=# INSERT INTO t1 VALUES('node2_data1');
INSERT 0 1
Primary2=# SELECT * FROM t1;
     c1      
-------------
 node1_data1
 node2_data1
(2 rows)

最後に、primary1のテーブルを確認してみましょう。primary2からデータがレプリケートされていることがわかります。

primary1=# SELECT * FROM t1;
     c1      
-------------
 node1_data1
 node2_data1
(2 rows)

3つめのプライマリーノードの追加

次の手順では、どのプライマリーノードにもテーブルt1にデータがないときに、既存のセットアップに新しいプライマリーノード(primary3)を追加する方法を示します。これには、ノードprimary1とprimary2にサブスクリプションを作成してprimary3からデータをレプリケートし、primary3にサブスクリプションを作成してprimary1とprimary2からデータをレプリケートする必要があります。
これらの手順は、primary1とprimary2間のレプリケーション設定がすでに完了していることを前提としています。

図4:3つのプライマリーノード間の双方向レプリケーション
図4:3つのプライマリーノード間の双方向レプリケーション

  1. ノードprimary3にパブリケーションを作成します。

primary3=# CREATE PUBLICATION pub_pri3 FOR TABLE t1;
CREATE PUBLICATION
  1. primary1からprimary3へのサブスクリプションを作成します。

primary1=# CREATE SUBSCRIPTION sub_pri1_pri3
primary1-# CONNECTION 'dbname=foo host=primary3 user=repuser'
primary1-# PUBLICATION pub_pri3 WITH (origin = NONE);
CREATE SUBSCRIPTION
  1. primary2からprimary3へのサブスクリプションを作成します。

primary2=# CREATE SUBSCRIPTION sub_pri2_pri3
primary2-# CONNECTION 'dbname=foo host=primary3 user=repuser'
primary2-# PUBLICATION pub_pri3 WITH (origin = NONE);
CREATE SUBSCRIPTION
  1. primary3からprimary1へのサブスクリプションを作成します。

primary3=# CREATE SUBSCRIPTION sub_pri3_pri1
primary3-# CONNECTION 'dbname=foo host=primary1 user=repuser'
primary3-# PUBLICATION pub_pri1 WITH (origin = NONE);
CREATE SUBSCRIPTION
  1. primary3からprimary2へのサブスクリプションを作成します。

primary3=# CREATE SUBSCRIPTION sub_pri3_pri2
primary3-# CONNECTION 'dbname=foo host=primary2 user=repuser'
primary3-# PUBLICATION pub_pri2 WITH (origin = NONE);
CREATE SUBSCRIPTION

これで、ノードprimary1、primary2、およびprimary3間のレプリケーションのセットアップが完了しました。プライマリーノードで行われた増分の更新データは、他の2つのノードにレプリケートされます。

制限事項

前述したレプリケーション設定の例には、いくつかの制限事項があります。

  • 既存のテーブルデータを持つ新しいプライマリーノードの追加はサポートされていません。

  • CREATE SUBSCRIPTION文でcopy_data=trueとorigin=NONEのパラメーターを組み合わせて使用する場合、初期同期テーブルデータはパブリッシャーから直接コピーされます。これは、そのデータの真のoriginを知ることができないことを意味します。
    パブリッシャーがサブスクリプション機能も持つ場合、コピーされたテーブルデータはさらに上流originから送信された可能性があります。このシナリオでは検出されて、ユーザーに警告が記録されますが、警告は潜在的な問題を引き起こす可能性にすぎません。コピーされたデータのoriginが本当に期待どおりであるかどうかを確認するのはユーザーの責任です。(パブリッシャーで作成された他のサブスクリプションが原因で)ローカル以外のoriginを含む可能性のあるテーブルを特定するには、次のSQLクエリを実行してください。


  • #以下の<pub-names>を照会するパブリケーション名に置き換えてください。
    SELECT DISTINCT PT.schemaname, PT.tablename
    FROM pg_publication_tables PT,
         pg_subscription_rel PS
         JOIN pg_class C ON (C.oid = PS.srrelid)
         JOIN pg_namespace N ON (N.oid = C.relnamespace)
    WHERE N.nspname = PT.schemaname AND
          C.relname = PT.tablename AND
          PT.pubname IN (<pub-names>);

注意事項

  • システムの双方向レプリケーションを設定する場合、ユーザーは論理レプリケーションのコンフリクトのリスクを最小限に抑える方法でスキーマを設計する必要があります。この問題の詳細については、PostgreSQL文書の「Conflicts」の節を参照してください。

  • 現在、論理レプリケーションにはいくつかの制限、または見方によっては不足している機能があります。詳細については、PostgreSQL文書の「Restrictions」の節を参照してください。

  • プライマリーノード間のレプリケーションを設定するには、さまざまなプライマリーノードで複数の手順を実行する必要があります。すべての操作がトランザクション処理されるわけではないため、バックアップを取ることをお勧めします。PostgreSQL文書の「Backup and Restore」の節で説明されているようにバックアップを取ることができます。

今後に向けて

「origin」パラメーターの追加は、マルチマスター論理レプリケーションをサポートするための第一歩です。この機能により、論理レプリケーションの柔軟性が向上します。富士通OSSチームはPostgreSQLコミュニティと共に、PostgreSQL論理レプリケーションの機能強化と追加を引き続き支援していきます。
この記事で説明したキーポイントの詳細については、以下を参照ください。

  • PostgreSQL文書のCREATE SUBSCRIPTION文のページでは、「origin」の使用方法に関する構文と詳細な仕様が記載されています。

2023年11月8日公開

オンデマンド(動画)セミナー

    • PostgreSQLに関連するセミナー動画を公開中。いつでもセミナーをご覧いただけます。
      • 【事例解説】運送業務改革をもたらす次世代の運送業界向けDXプラットフォームの構築
      • ハイブリッドクラウドに最適なOSSベースのデータベースご紹介

本コンテンツに関するお問い合わせ

お電話でのお問い合わせ

Webでのお問い合わせ

当社はセキュリティ保護の観点からSSL技術を使用しております。