PostgreSQL 実践講座 #8 レプリケーションと高可用性 — ストリーミングレプリケーション、読み取り分散、フェイルオーバー
ここまでの話はすべてサーバー 1 台の中でした。しかし 1 台には 2 つの限界があります。死んだらサービスが止まり(可用性)、読み取りの負荷が集中したらこれ以上大きくする場所がありません(スケーリング)。2 つの問題の標準手段がレプリケーション、つまり同じデータを持つサーバーを複数置くことです。原理を知っておけば、マネージドサービスのオプションたち(RDS Multi-AZ vs リードレプリカで扱ったあの選択肢たち)が「どの層を売ってくれる商品なのか」鮮明になります。
原理: WAL を流す #
PostgreSQL はすべての変更を、コミットの前に WAL(Write-Ahead Log)という順次ログへ先に書きます(障害復旧の根拠であり、次回の PITR の材料です)。ストリーミングレプリケーションは、この WAL をネットワークでスタンバイサーバーへリアルタイムに流し、スタンバイがそれを再生するものです。 バイトレベルのコピーが作られるので、物理レプリケーションとも呼ばれます。スタンバイは読み取り専用のクエリを受けられるので(ホットスタンバイ)、待機装備でありながら同時に読み取り分散の手段にもなります。
非同期が既定値、そしてレプリケーション遅延 #
既定の動作は非同期です。プライマリはコミットをスタンバイへの到着と無関係に完了するので書き込み性能に負担はありませんが、2 つの代価があります。
- 障害時の消失の窓: プライマリが死んだ瞬間、まだ渡っていない WAL の分のコミットが消えることがあります。
- レプリケーション遅延(replication lag): スタンバイはいつも少しだけ過去です。普段はミリ秒レベルですが、負荷によっては秒単位に開きます。
遅延の実務での症状が read-your-writes 問題です。ユーザーが投稿して(プライマリ)、一覧を更新したら(スタンバイ)自分の投稿がありません。読み取り分散を導入した瞬間にアプリケーションが引き受ける問題で、通常の解法は「書いた直後の読み取りはプライマリへ送る」というルーティングのルールです。遅延の観測はプライマリの pg_stat_replication ビューでやります。
SELECT client_addr, state,
pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn) AS lag_bytes
FROM pg_stat_replication;消失の窓が許されないドメイン(決済など)には同期レプリケーション(synchronous_commit)があります。スタンバイの確認後にコミットを完了するので消失はなくなる代わりに、すべてのコミットに往復の遅延が付き、スタンバイの障害が書き込みの障害に広がります。「全部同期」ではなくトランザクション単位で選ぶこともできるので、必要なところにだけ代価を払うのが定石です。
フェイルオーバー: 昇格は簡単で、判断が難しい #
プライマリが死んだら、スタンバイを昇格(promote)させて新しいプライマリにします。昇格自体はコマンド 1 つです。難しいのはその前後です。死んだという判断(ネットワークの瞬断か本当の障害か)、アプリケーションの接続の切り替え、そして最悪の事故であるスプリットブレイン(旧プライマリが生きているのに新プライマリも書き込みを受けて、データが枝分かれすること)の防止です。だから自動フェイルオーバーはスクリプトではなく、合意ベースのオーケストレーターに任せます。自前運用の事実上の標準が Patroni(分散合意ストアでリーダーを管理)で、マネージドサービスの Multi-AZ 自動フェイルオーバーは、まさにこの層を丸ごと売ってくれる商品です。RDS vs 自前運用の計算でこの項目の重みが大きい理由です。
論理レプリケーション: テーブル単位で選んで送る #
物理レプリケーションが「クラスタ全体のバイトのコピー」なら、論理レプリケーションはテーブル単位で変更の内容(行の INSERT/UPDATE/DELETE)を発行(publication)して購読(subscription)する方式です。使いどころが違います。一部のテーブルだけ分析用 DB へ送る、メジャーバージョンが違うサーバーへのレプリケーション(無停止アップグレードの材料。次回扱います)、複数の DB のデータ統合といった席です。スタンバイを丸ごと作るのが目的なら物理、データの一部を別の形のシステムへ運ぶのが目的なら論理、と覚えておけば十分です。
まとめ #
- レプリケーションの原理は WAL のストリーミングです。バイトのコピー(物理)が作られ、スタンバイは読み取りを受けられます。
- 基本は非同期で、代価は消失の窓とレプリケーション遅延です。読み取り分散は read-your-writes のルーティングとセットです。
- 消失が許されないトランザクションにだけ同期レプリケーションの代価を払います。全部同期は過剰です。
- フェイルオーバーの本体は昇格ではなく、判断・切り替え・スプリットブレインの防止です。自前運用は Patroni、マネージドは Multi-AZ がその答えです。
- テーブル単位・バージョン跨ぎは論理レプリケーションの席です。次回、WAL を材料にしたバックアップ・PITR・アップグレードでシリーズを締めくくります。