Menu

PostgreSQL論理レプリケーション設定

PostgreSQL v10で実装されたロジカルレプリケーション機能を利用すると、テーブル単位でレプリケーション構成をとれます。
一般的なRead/Writeスプリット構成のほか、アプリケーションフレームワークから見て単一DB内に外部マスターのテーブルを持ち込むこともできるため、マイクロサービスの難題になりがちなデータ伝播の解決への活用も可能です。

セットアップの大まかな流れは以下のとおりです。

  1. postgresql.conf設定
  2. 元DB:接続セキュリティ設定
  3. 複製先:テーブル作成(スキーマ定義)
  4. 元DB:PUBLICATION作成
  5. 複製先:SUBSCRIPTION作成

postgresql.conf設定

ロジカルレプリケーションが利用するリソースを設定しておきます。設定を有効にするためには再起動が必要です。

関連する項目には以下のようなものがありますが、設定値はとくに推奨値ではなく、枯渇しない値であれば動作します。

wal_level = logical
max_worker_processes = 8
max_wal_senders = 10
max_replication_slots = 10
max_logical_replication_workers = 4

pg_hba.conf設定

一般的な接続に関するセキュリティ設定として、必要に応じて元DBでレプリケーション処理を実行するDBユーザーを作成し、テーブルへのアクセス権を設定します。

postgres=# CREATE ROLE rep_user PASSWORD 'testpassword' LOGIN REPLICATION;
CREATE ROLE

postgres=# \c origin_db
You are now connected to database "origin_db" as user "postgres".

origin_db=# GRANT SELECT ON users, articles TO rep_user;
GRANT

また、pg_hba.confでDBユーザーが接続できるようネットワーク設定も必要です。

複製先スキーマ作成

転送するテーブルと同一のスキーマで複製先に空のテーブルを作成しておきます。
テーブルスキーマをRubyDSLで書けるridgepoleを活用すると2つのスキーマのメンテナンスがDRYになります。

ロジカルレプリケーションでは元のDBと連動してINSERTなどが走りますが、ここで作成したスキーマのユニーク制約などに抵触するとエラーになるので、目的によっては元のDBよりも制約を緩くしておくという考え方もありそうです。

また、ロジカルレプリケーションでは定常的なWrite処理が流れてくるものの、実際には複製先は独立したテーブルで、元DBにはないカラムを追加して変更することも可能です(データ不整合のリスクあり)。

PUBLICATION作成

論理レプリケーションは、元DBでPUBLICATIONを作成し、複製先にSUBSCRIPTIONという通信チャネルを作成することで接続できます。

CREATE PUBLICATIONで配信するテーブルを指定して接続を受け付けるチャネルを作成します。
PUBLICATIONの名称は任意で、のちにSUBSCRIPTION作成時に指定します。

origin_db=# CREATE PUBLICATION origindb_pub FOR TABLE users, articles;
CREATE PUBLICATION

なお、同一クラスタ内・同一DB上でレプリケーションを作成する場合、以下のとおりロジカルでコーディングのレプリケーションスロットを手動で作成する必要があります。(CREATE SUBSCRIPTION公式マニュアルのNOTES欄に解説があります)

origin_db=# SELECT pg_create_logical_replication_slot('origin_sub1', 'pgoutput'); 
 pg_create_logical_replication_slot 
------------------------------------
 (assess_sub1,0/9C867F0)
(1 row)

SUBSCRIPTION作成

最終的に複製先のDBでCREATE SUBSCRIPTIONします。
SUBSCRIPTION名は任意ですが、同一DBのレプリケーション構成の場合には、オリジンDB上で作成したロジカルレプリケーションスロット名と合わせます(別名をつけるオプションもあります)。また、この構成ではWITH (create_slot=false)オプションも必要です。

origin_db=# \c copy_db;

CREATE SUBSCRIPTION origin_sub1 CONNECTION 'host=127.0.0.1  dbname=origin_db user=rep_user password=testpassword' PUBLICATION origindb_pub WITH (create_slot=false);

デフォルトでは、SUBSCRIPTIONを作成するとすぐに転送が始まります。
初期同期時には以下のようなログが出力されるか、接続できない場合はエラーが出力されます。

2018-03-06 06:53:51.146 UTC [871] LOG:  logical replication apply worker for subscription "origin_sub1" has started
2018-03-06 06:53:51.174 UTC [872] LOG:  starting logical decoding for slot "origin_sub1"
2018-03-06 06:53:51.174 UTC [872] DETAIL:  streaming transactions committing after 0/A68B270, reading WAL from 0/A68B238
2018-03-06 06:53:51.181 UTC [872] LOG:  logical decoding found consistent point at 0/A68B238
2018-03-06 06:53:51.181 UTC [872] DETAIL:  There are no running transactions.
2018-03-06 06:53:51.219 UTC [873] LOG:  logical replication table synchronization worker for subscription "origin_sub1", table "users" has started
2018-03-06 06:53:51.220 UTC [874] LOG:  logical replication table synchronization worker for subscription "origin_sub1", table "articles" has started
2018-03-06 06:53:51.276 UTC [876] LOG:  logical decoding found consistent point at 0/A690410
2018-03-06 06:53:51.276 UTC [876] DETAIL:  There are no running transactions.
2018-03-06 06:53:51.297 UTC [875] LOG:  logical decoding found consistent point at 0/A690448
2018-03-06 06:53:51.297 UTC [875] DETAIL:  There are no running transactions.
2018-03-06 06:53:51.517 UTC [873] LOG:  logical replication table synchronization worker for subscription "origin_sub1", table "users" has finished

まとめ:使いどころ

マイクロサービスとしてサービス開発した場合、データのサイロがネックになります。REST APIで結合できる範囲であれば問題ありませんが、より高度な条件でデータを統合したいケースではRDBMSレイヤのレプリケーションは非常に強力です。

Railsなどのアプリケーションから見ると1テーブル増えただけの構成となるため、ActiveRecordモデルを追加するだけで既存テーブルとのアソシエーションや複雑なJOINへの道が開けます。

全件転送する性質上、極端にWRITE処理に偏るデータソースには向きませんが、参照頻度の高いデータをネットワーク越しに配布するケースでは有力な選択肢と言えます。

参考資料