内部構造から学ぶPostgreSQL 設計・運用計画の鉄則 ch10 (高可用化と負荷分散) (4/6)

OSS-DBPostgreSQL勉強メモ

出典: 


ストリーミングレプリケーションの運用

フェイルオーバ時の処理

  • 自動ではやってくれない

    • 自作
    • ありもののHAクラスタソフトウェアをつかう

プライマリの故障時

  • スタンバイをプライマリに昇格する

    • 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/に無いことがある

    • レプリケーションスロットで回避可能

      • 公式
      • すべてのスタンバイでWALを受信するまでwalをpg_wal/から消さない
      • プライマリのwal領域がディスクフルになるリスクもある
  • スタンバイのarchive_modealwaysにすることで、スタンバイ側でもWALをアーカイブできる

  • onのイメージ

    • standby側にアーカイブがないのでリカバリのしようがない
master
    somewhere/archive/
    pg_wal/

standby
    pg_wal/
  • alwaysのイメージ

    • standby側にもアーカイブがあり、そこからリカバリできる
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秒のデータの遅れが致命的な場合はもっと短くする

vacuumdefercleanup_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以上にすると、古いバージョンのタプルも指定の数だけ残す

hotstandbyfeedbackパラメータ

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)
  • デフォルト無効

    • プライマリはスタンバイのことを関知しない
  • 有効にすると、プライマリはスタンバイが受けている問い合わせについての情報を受け取る

    • スタンバイが必要としている = 削除するとコンフリクトしうる行の削除を遅らせる