論理レプリケーションにおける通信の改善 - PostgreSQL 15でコミットされた機能の先行紹介:技術者Blog
PostgreSQLインサイド


Wang Wei
南京富士通南大軟件技術有限公司
開発事業本部デジタルソリューション事業部
はじめに
このブログでは、論理レプリケーションの通信における次の2つの問題を解決するために、PostgreSQL 15で実施された改善について説明します。
- トランザクション内のすべてのDMLがパブリケーションのフィルター条件に従ってパブリッシュされない場合、walsenderは空のトランザクションを送信します。これにより、CPU / メモリー / ネットワークなどのリソースが無駄になります。
- 大規模なトランザクションを処理する際、walsenderがトランザクション内のパブリッシュされないDMLの処理でビジー状態の場合、長時間にわたってwalreceiverと通信できなくなることがあります。これにより、walsenderが正しく動作していても、予期せぬタイムアウトエラーが発生することがあります。
目次
論理レプリケーションにおける通信の仕組み
まずは、論理レプリケーションにおける通信に関する2つの概念、「通信メッセージの種類」と「フィルタリング」について簡単に紹介します。
通信メッセージの種類
論理レプリケーションでは、walsenderからwalreceiverに送信されるメッセージには2種類あります。
-
キープアライブ(keepalive)メッセージ
このメッセージは、walsenderが正しく動作していることをwalreceiverに伝えるために使用されます。
ユーザーは、wal_receiver_timeoutパラメーター(デフォルトは60秒)を使って、walreceiverのタイムアウトの時間を設定できます。wal_receiver_timeoutの時間内に、walreceiverがwalsenderからいずれの種類のメッセージも受信しない場合、タイムアウトエラーで終了します。 -
論理レプリケーションのプロトコルメッセージ
論理レプリケーションのプロトコルメッセージには多くの種類がありますが、このブログではそのうちの2つだけを取り上げます。
- INSERT、UPDATE、DELETE、およびTRUNCATEを含むDMLメッセージ
- BEGINメッセージやCOMMITメッセージなどのトランザクションの開始と終了を定義するメッセージ
参考
論理レプリケーションの全プロトコル一覧は、PostgreSQLのWebサイトで確認できます。
- 55.5. Logical Streaming Replication Protocol(PostgreSQLオフィシャルのページへ)
フィルタリング
トランザクション内のすべてのDMLがwalreceiverに送信されるわけではありません。これは、パブリケーションの作成時に、テーブル、行、または操作の種類に対してフィルターを指定できるためです。フィルター条件を満たすと、一部のDMLは送信されません。なお、PostgreSQL 15の行フィルターについては、「論理レプリケーションにおけるパブリケーションの行フィルター」というブログ記事を参照してください。
改善の概要
では、このブログの冒頭で述べた2つの問題点がどのように改善・修正されたかをご紹介します。
空のトランザクションに対する改善
トランザクション内のすべてのDMLがデコード時にフィルタリング(除外)される場合、このトランザクションを「空のトランザクション」と呼びます。PostgreSQL 14以前では、標準のロジカルデコードプラグインであるpgoutput(PostgreSQLにデフォルトで使用されている論理レプリケーションプラグイン)は、すべてのトランザクションをwalreceiverに送信していました。空のトランザクションの場合でも、DML関連のメッセージは送信されませんが、トランザクションの開始と終了を定義するメッセージは依然として送信されました(図1参照)。このような空のトランザクションを構築・送信することは、CPUサイクルとネットワーク帯域幅の無駄遣いでした。
図1:PostgreSQL 14以前における空のトランザクションの扱い
この問題を解決するために、最初のDMLメッセージが送信されるまでBEGINメッセージを延期するようにwalsenderを改善しました。デコード終了時に、BEGINメッセージが送信されていなければ、COMMITメッセージも送信されないようにしました。詳細な開発情報については、GitHubを参照してください。
- Skip empty transactions for logical replication(GitHubのページへ)
また、空でないトランザクションについても、メッセージを送信するタイミングが変更されます。このPostgreSQL 14とPostgreSQL 15の違いを例で見てみましょう。次のようなトランザクションT1があるとします。
postgres=# BEGIN;
BEGIN
postgres=*# INSERT INTO tab_not_publish VALUES (1); -- このDMLはフィルタリング(除外)される
INSERT 0 1
postgres=*# INSERT INTO tab_publish VALUES (1); -- このDMLはパブリッシュされる
INSERT 0 1
postgres=*# COMMIT;
COMMIT
BEGINメッセージとCOMMITメッセージの送信タイミングを、次の図2に示します。
図2:BEGINメッセージとCOMMITメッセージの送信タイミング - 修正前
図2からわかるように、修正前はwalsenderからwalreceiverに送信できるのは、テーブルtab_publishのINSERTメッセージだけです。
修正後のBEGINメッセージとCOMMITメッセージの送信タイミングを、次の図3に示します。
図3:BEGINメッセージとCOMMITメッセージの送信タイミング - 修正後
図3からわかるように、修正後では、もしトランザクション内のすべてのDMLがフィルタリング(除外)されると、BEGINメッセージとCOMMITメッセージは送信されません。このようにして、walsenderは空のトランザクションの送信をスキップできます。
ただし、PostgreSQL 14以前の同期論理レプリケーションでは、データが同期されたことを確認するために、walreceiverは空のトランザクションのCOMMITメッセージを受信すると、ローカルデータを同期し、walsenderにフィードバックメッセージを送信しています。
-
補足
同期論理レプリケーションについては、PostgreSQLオフィシャルのページをご覧ください。
- 27.2.8. Synchronous Replication(PostgreSQLオフィシャルのページへ)
walsenderは、walreceiverからフィードバックメッセージを受け取った後のみ処理を続行し、それ以外の場合、walsenderはトランザクションをコミットするためにClient backendをブロックします。
そこで、PostgreSQL 15では、walsenderは同期論理レプリケーションで空のトランザクションをスキップした後、walreceiverにキープアライブメッセージを送信し、フィードバックメッセージを要求するようにしました。そして、walreceiverはデータを同期し、このメッセージに従ってwalsenderにフィードバックメッセージを送信します。このようにして、同期論理レプリケーションでは、空のトランザクションによって応答が遅延する事態を回避できます。次の図4と図5は、同期論理レプリケーションにおける通信の違いを示しています。
図4:同期論理レプリケーションにおける通信 - 修正前
図5:同期論理レプリケーションにおける通信 - 修正後
予期せぬタイムアウトエラーの修正
PostgreSQL 14以前では、トランザクションにパブリッシュされない連続したDMLが多数ある場合、walsenderはこれらのパブリッシュされないDMLのデコード処理でビジー状態になるため、長時間にわたってwalreceiverと通信できなくなります。この場合、walsenderが正しく動作していても、walreceiverは指定されたタイムアウト内にwalsenderからメッセージを受信しないため、walreceiverに予期せぬタイムアウトエラーが発生する原因となっていました。
PostgreSQL 15では、このエラーを回避するために、walsenderはwalreceiverと定期的に通信し続けます。そのため、walsenderはDMLの処理数が特定の閾値に達すると(それらのDMLがパブリッシュされているかどうかに関わらず)、必要に応じて通信を維持するためにwalreceiverにキープアライブメッセージを送信しようと試みます。この開発に関する詳細な情報については、GitHubで参照できます。
次のようなたくさんのDMLがあり、どれもパブリッシュされないトランザクションがあったとします。次の図6と図7でわかるように、トランザクションが処理されるときの、walsenderとwalreceiverの間の通信が変更されました。
図6:walsenderとwalreceiverの間の通信 - 修正前
図7:walsenderとwalreceiverの間の通信 - 修正後
上記からわかるように、PostgreSQLカーネルでは閾値を100に設定しています(性能テストの結果、この閾値によってこのタイムアウトエラーを解決でき、性能が低下しないことが確認されています)。こうすることで、walsenderが正しく動作している場合、walreceiverに予期しないタイムアウトエラーは発生しません。
-
補足
閾値については、PostgreSQLオフィシャルのページをご覧ください。
- RE: Logical replication timeout problem(PostgreSQLオフィシャルのページへ)
性能への影響
最後に、空のトランザクションに対する改善に関する性能テストの結果を共有します。
前述のとおり、空のトランザクションの処理を改善した結果、walsenderはBEGINとCOMMITメッセージのみを含む空のトランザクションについてはwalreceiverに送信しなくなりました。これにより、ネットワークトラフィックが減少し、性能が向上します。私のテストでは、改善後、空のトランザクションをデコードする場合、非同期論理レプリケーションではwalsenderの送信量は97バイト減少しました。また、同期論理レプリケーションではwalsenderがキープアライブメッセージを追加送信するものの、合計送信量は79バイト減少しました。次に、ネットワーク帯域消費における性能(パフォーマンス)の向上について、テスト結果を通して見てみましょう。
walsenderが送信するトランザクションに占める空のトランザクションの割合がテスト結果に影響するため、空のトランザクションの割合を5段階に分けてテストしました。また、改善後の同期論理レプリケーションでは、walsenderが空のトランザクションをスキップした場合、キープアライブメッセージを追加送信するため、同期論理レプリケーションと非同期論理レプリケーションの両方でテストを実施しました。次の図8と図9に示すように、非同期論理レプリケーションと同期論理レプリケーションの両方で性能が向上していることがわかります(横軸は送信トランザクションに占める空のトランザクションの割合を表します。縦軸はwalsenderからwalreceiverに転送されたデータの総量をバイトで表しています。)
図8:性能比較 - 非同期論理レプリケーション
図9:性能比較 - 同期論理レプリケーション
テスト結果から、空のトランザクションの割合が高いほど、改善効果は大きいことがわかります。空のトランザクションの割合が25%、50%、75%、100%の場合、ネットワーク通信量はそれぞれ約8%、22%、40%、84%削減されました。また、空のトランザクションがない場合、性能の劣化はありません。
今後に向けて
今回は、空のトランザクションのスキップや、予期せぬタイムアウトエラーの修正による論理レプリケーションの性能・機能の向上について紹介しました。しかし、空のトランザクションをスキップするメカニズムには、まだいくつかの制限があります。例えば、空のトランザクションは二相コミットではスキップされません。これは、トランザクションを準備してからプリペアドトランザクションをコミットするまでの間にwalsenderが再起動した場合、プリペアドトランザクションがコミット時にスキップされたかどうかが明確にわからないためです。進行中のトランザクションのストリーミング(subscription_parameter のパラメーター"streaming"で設定)についても、同様の問題により改善されていません。近いうちに、上記2種類のトランザクションのうち、空のトランザクションの処理について、より良い方法での改善を試みる可能性があります。
さらに、walreceiverにおける進行中のトランザクションのストリーミング適用の効率を改善するために、私たちのチームはwalreceiverにおけるトランザクションの並列適用に関するパッチを共有し、コミュニティーで活発に議論して、継続的に改善しています。
-
補足
議論の状況については、PostgreSQLオフィシャルのページをご覧ください。
- Perform streaming logical transactions by background workers and parallel apply(PostgreSQLオフィシャルのページへ)
2022年10月7日公開
富士通のソフトウェア公式チャンネル(YouTube)
-
- 富士通のミドルウェア製品のご紹介や各種イベント・セミナーの講演内容、デモンストレーションなどの動画をご覧いただけます。
- 富士通のミドルウェア製品のご紹介や各種イベント・セミナーの講演内容、デモンストレーションなどの動画をご覧いただけます。
PostgreSQLについてより深く知る
PostgreSQLに興味をお持ちのお客様はこちらのコンテンツもお勧めです。ぜひご覧ください。
本コンテンツに関するお問い合わせ
お電話でのお問い合わせ
-
富士通コンタクトライン(総合窓口)
0120-933-200受付時間:9時~12時および13時~17時30分(土曜日・日曜日・祝日・当社指定の休業日を除く)