本エントリーはPostgreSQL Advent Calendar 2021の11日目です。
はじめに
PostgreSQLのレプリケーションには、ストリーミングレプリケーションとロジカルレプリケーションの二つのレプリケーションが存在します。
本エントリーではロジカルレプリケーションを使用するにあたり、自身が経験した注意するべき事項を解説します。
(本業が多忙により、図の作成を行うことができませんでした…)
ロジカルレプリケーションとは
ロジカルレプリケーションは通常(ストリーミング)のレプリケーションと異なり、データベース全体の複製を行いません。
レプリケーション元をパブリッシャー、レプリケーション先をサブスクライバーと呼びます。
サブスクライバーは連携対象のテーブルを選択でき、検索だけではなく更新等も可能となります。
パブリッシャーで更新が発生すると、生成されるログ(WAL(Write Ahead Log))をデコードして更新内容をサブスクライバーに送信します。
サブスクライバーは受信した更新内容を自身のデータベースに反映します。
ロジカルレプリケーション使用時の注意点
サブスクライバー側でテーブル更新を行うと、コンフリクトが発生してレプリケーションが停止する可能性がある
サブスクライバー側でレプリケーション対象テーブルに対してデータ追加を行い、パブリッシャー側で同一主キーのデータ追加を行うとコンフリクトが発生します。
コンフリクトが発生するとレプリケーションが停止し、他テーブルの更新も止まってしまいます。
また、パブリッシャー側では連携待ちのWALが溜まっていくことになります。
解決策
サブスクライバー側のログを確認し、エラーとなっているデータを確認して削除を行います。
コンフリクトデータが多量にある場合の手っ取り早い解決策は以下の手順となります。
- パブリッシャー側で、コンフリクト発生テーブルをレプリケーション対象外にする。(ALTER PUBLICATION name DROP TABLE table_name)
- サブスクライバー側で、ロジカルレプリケーション情報を更新する。(ALTER SUBSCRIPTION name REFRESH PUBLICATION)
- サブスクライバー側で、コンフリクト発生テーブルのデータを全消去(TRUNCATE)する。
- パブリッシャー側で、コンフリクト発生テーブルをレプリケーション対象にする。(ALTER PUBLICATION name ADD TABLE table_name)
- サブスクライバー側で、ロジカルレプリケーション情報を更新する。(ALTER SUBSCRIPTION name REFRESH PUBLICATION)
マテリアライズドビューは連携対象外
VIEWの内容を永続化できる便利なマテリアライズドビューですが、パブリッシャー側で作成したマテリアライズドビューはレプリケーション対象外です。
解決策
postgres_fdwを使用して、サブスクライバー側からアクセスできるようにしてください。
高速化を求める場合はサブスクライバー側にもマテリアライズドビューを作成し、外部テーブルの内容を取得・更新するようにするとよいでしょう。
1トランザクション中に膨大なデータを更新すると、後続データの連携に遅延が発生する
1トランザクションで数十GBといった膨大なデータを更新すると、レプリケーション遅延が発生します。
これは、WALをデコードする部分がシングルプロセスで動いているためです。
レプリケーション対象外のテーブル更新でも起こります。
解決策
大規模な更新が発生しうるテーブルは、unloggedテーブルにすることをお勧めします。
unloggedテーブルの場合、データベースの再起動でデータが消去されるため、復旧する手段を別途構築する必要があります。
該当テーブルを別データベースクラスタ化してしまい、postgres_fdwでアクセスできるようにする方法もあります。
終わりに
ロジカルレプリケーションはとても便利で使い勝手の良いレプリケーションですが、使ってわかる落とし穴があったりします。
似たような事例で悩んでいる方の参考になればと思います。