内部構造から学ぶPostgreSQL 設計・運用計画の鉄則 ch10 (高可用化と負荷分散) (4/6)
ストリーミングレプリケーションの運用
フェイルオーバ時の処理
プライマリの故障時
- スタンバイをプライマリに昇格する
pg_ctl promote
コマンド- トリガーファイルを置く
- ファイル名は
promote_trigger_file
で設定しておく
- ファイル名は
pg_promote
関数
- 「クライアントにcommitが返却されていないが、スタンバイのWALは更新されている」という状態がありうることに注意する
スタンバイの故障時
- 同期レプリケーションの場合、同期のスタンバイすべてが停止してしまうと、プライマリではコミットを完了できなくなってしまう
- のでプライマリの設定が必要
synchronous_standby_names
#synchronous_standby_names = '' # standby servers that provide sync rep # method to choose sync standbys, number of sync standbys, # and comma-separated list of application_name # from standby(s); '*' = all
- reload必要
postgres=# SELECT name,setting,context FROM pg_settings WHERE name='synchronous_standby_names'; name | setting | context ---------------------------+---------+--------- synchronous_standby_names | | sighup (1 row)
プライマリ/スタンバイの監視
walsender/walreceiverプロセスの動作確認
docker-compose exec master ps axfww | grep walsender
97 ? Ss 0:00 postgres: walsender replication_user 172.18.0.3(42974) streaming 0/3000148
docker-compose exec standby1 ps axfww | grep walreceiver
20 ? Ss 0:00 \_ postgres: walreceiver streaming 0/3000148
- プライマリでタイムアウトが発生すると
master_1 | 2020-02-22 12:50:05.497 UTC [96] LOG: terminating walsender process due to replication timeout
- スタンバイでタイムアウトが発生すると
standby1_1 | 2020-02-22 12:50:05.534 UTC [21] FATAL: terminating walreceiver due to timeout
- 【補】docker環境でネットワーク障害をエミュレートする
docker network disconnect NETWORK CONTAINER
レプリケーションの状況確認
- primaryの
pg_stat_replication
ビューを読む方法
postgres=# SELECT flush_lsn, replay_lsn FROM pg_stat_replication; flush_lsn | replay_lsn -----------+------------ 0/3013558 | 0/3013558 (1 row)
- 更新タイミング
- writeまたはflushの位置に変更があったとき
wal_receiver_status_interval
による設定時間が経過したら- デフォルト10秒
#wal_receiver_status_interval = 10s # send replies at least this often
postgres=# SELECT name,setting,context FROM pg_settings WHERE name='wal_receiver_status_interval'; name | setting | context ------------------------------+---------+--------- wal_receiver_status_interval | 10 | sighup (1 row)
- standbyで直接receive位置、replay位置を確認する方法
postgres=# SELECT pg_last_wal_receive_lsn(), pg_last_wal_replay_lsn(); pg_last_wal_receive_lsn | pg_last_wal_replay_lsn -------------------------+------------------------ 0/3013470 | 0/3013470 (1 row)
プライマリ/スタンバイの再組み込み時の注意点
故障したスタンバイの再組み込み
- スタンバイがプライマリよりも進んでしまうことはないので比較的容易
- スタンバイを再起動するだけ
- 届いていないWALレコードがプライマリにあれば自動的に転送される
- プライマリのwalレコードがアーカイブされていて
pb_wal/
に無いことがある - スタンバイの
archive_mode
をalways
にすることで、スタンバイ側でもWALをアーカイブできる - onのイメージ
master somewhere/archive/ pg_wal/ standby pg_wal/
master somewhere/archive/ pg_wal/ standby somewhere/archive/ pg_wal/
故障したプライマリの再組み込み
- 故障したプライマリ(旧プライマリ、新スタンバイ)のほうが、昇格するスタンバイ(旧スタンバイ、新プライマリ)よりも進んでいることがある
- スタンバイのほうが進んでしまっていると、レプリケーションを継続できない
- 新プライマリからペースバックアップを取り直して再構築しなければならない
コンフリクトの緩和策
postgres=# SELECT name,setting,context,unit FROM pg_settings WHERE name='max_standby_archive_delay'; name | setting | context | unit ---------------------------+---------+---------+------ max_standby_archive_delay | 30000 | sighup | ms (1 row) postgres=# SELECT name,setting,context,unit FROM pg_settings WHERE name='max_standby_streaming_delay'; name | setting | context | unit -----------------------------+---------+---------+------ max_standby_streaming_delay | 30000 | sighup | ms (1 row)
- コンフリクト発生時のWAL適用待ち時間
- プライマリでの更新操作(リレーションの削除等)のWALをスタンバイに送信
- スタンバイではWALの受信まではできるが、共有ロックと競合して適用できないやつ
- 適用待ち時間を上記コンフィグパラメータで設定する
- デフォルト30秒、ミリ秒単位で設定可能
- 30秒のデータの遅れが致命的な場合はもっと短くする
vacuum_defer_cleanup_ageパラメータ
- 前提として、物理的な行削除がコンフリクトを起こす
- VACUUM FULLやHOTの行削除がコンフリクト
- 単なるUPDATEやDELETEではコンフリクトしない
- 追記型アーキテクチャでMVCCを実現しているから
- 物理的な行削除を遅延することで、コンフリクトを軽減する
- プライマリの
vacuum_defer_cleanup_age
オプション - 公式
#vacuum_defer_cleanup_age = 0 # number of xacts by which cleanup is delayed
postgres=# SELECT name,setting,context FROM pg_settings WHERE name='vacuum_defer_cleanup_age'; name | setting | context --------------------------+---------+--------- vacuum_defer_cleanup_age | 0 | sighup (1 row)
- デフォルト0
- 可視タプル以外1つ残らず消す
- 1以上にすると、古いバージョンのタプルも指定の数だけ残す
hot_standby_feedbackパラメータ
postgres=# SELECT name,setting,context,vartype FROM pg_settings WHERE name='hot_standby_feedback'; name | setting | context | vartype ----------------------+---------+---------+--------- hot_standby_feedback | off | sighup | bool (1 row)
- デフォルト無効
- プライマリはスタンバイのことを関知しない
- 有効にすると、プライマリはスタンバイが受けている問い合わせについての情報を受け取る
- スタンバイが必要としている = 削除するとコンフリクトしうる行の削除を遅らせる