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

OSS-DBPostgreSQL勉強メモ

出典: 


サーバの役割と呼び名

  • 更新可否による分類

    • master/primary

      • 読み書きできるやつ
    • standby/secondary

      • プライマリ側のデータ変更を追跡するやつ
      • PostgreSQL 9.6まではslaveだった
      • PostgreSQL 10からsecondary
  • standbyのさらなる分類

    • warm standby

      • masterに昇格するまでクライアントから接続できないやつ
    • hot standby

      • master昇格前もクライアントから接続できる

        • 読み取りのみ
  • 実現方法いろいろ

    • 公式/実現方法の比較
    • Shared Disk Failover

      • プライマリとセカンダリでデータベースクラスタを共有するやつ

        • ネットワークストレージデバイスとかで
      • プライマリ稼働中はセカンダリは停止していること

        • 【所感】postmaster.pidがあるからかな
    • File System (Block Device) Replication

      • 共有ディスク相当の機能をソフトウェアレベルで実装する

    • Write-Ahead Log Shipping

      • PITRと同様の原理でのレプリケーション
      • WALの処理は同期/非同期で行うことができる

        • 後述
      • データベースクラスタ全体についてレプリケーションする

        • テーブル単位とかはできない
      • 実装

        • file-based log shipping
        • streaming replication
    • Logical Replication

      • PostgreSQL 10からサポートされたやつ
      • 物理的なWALそのままではなく、論理的なデータ変更を流す
    • Trigger-Based Master-Standby Replication

      • Slony-Iとか

        • テーブル単位の非同期maser/slaveレプリケーションシステム
        • PostgreSQL 9.0でStreaming ReplicationがPostgreSQL本体に搭載されるまで活躍していた
    • Statement-Based Replication Middleware

      • SQLクエリをミドルウェアでinterceptして、masterとsecondaryへクエリを流す
      • 各サーバーが独立してSQLを実行するので、非決定的な関数は、サーバーごとに異なる値になる

        • random()
        • CURRENT_TIMESTAMP
    • Asynchronous Multimaster Replication

      • Bucardoとか
      • 常には接続されていない、コネクションが遅い場合等、常に同期をとるのが難しい場合に
      • 各サーバー独立して動作し、定期的にサーバ間で通信してコンフリクト検出・解決する
      • 【所感】gitににてる
    • Synchronous Multimaster Replication

      • 読み書きサーバーが複数あるやつ
      • データ変更を伝播するので、random()等の非決定的な関数もだいじょうぶ
  • 本章の内容

    • ストリーミングレプリケーション for 高可用化
    • ロジカルレプリケーション for 負荷分散

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

  • OSS-DB道場10
  • OSS-DB道場11
  • WALをファイル単位ではなく変更内容(WALレコード)ごとに流すやつ
  • れきし

    • PostgreSQL 9.0 非同期レプリケーション
    • PostgreSQL 9.1 同期レプリケーション
    • PostgreSQL 9.2 カスケード, remote_write

ストリーミングレプリケーションの仕組み

  • PITRと同様の原理
  • primaryで生成されたWALをsecondaryでリプレイ

WALの特性

  • 公式/WALと信頼性
  • 性能を担保しつつ永続性を保証するやつ
  • 性能

    • トランザクションのたびにデータページを逐一同期的に書き込むのは高コスト

      • 小さなトランザクションが頻発し、データのあちこちを更新する場合は特にそう
    • WALのみを同期書き込みする

      • 遥かに低コスト
  • 永続性

    • リカバリ時には、必要なLSN位置からWALレコードを順にリプレイしてロールフォワードする
    • LSN: Log Sequence Number

      • WALレコードに払い出される一意な値

ストリーミングレプリケーションの制限

  • WALの特性を利用するので、WALレコードを出力しないunloggedテーブルなどはストリーミングレプリケーションされない
  • create table文でunlogged設定可能
Description: define a new table
Syntax:
CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXISTS ] table_name ( [
  { column_name data_type [ COLLATE collation ] [ column_constraint [ ... ] ]
    | table_constraint
    | LIKE source_table [ like_option ... ] }
    [, ... ]
] )
[ INHERITS ( parent_table [, ... ] ) ]
[ PARTITION BY { RANGE | LIST | HASH } ( { column_name | ( expression ) } [ COLLATE collation ] [ opclass ] [, ... ] ) ]
[ USING method ]
[ WITH ( storage_parameter [= value] [, ... ] ) | WITHOUT OIDS ]
[ ON COMMIT { PRESERVE ROWS | DELETE ROWS | DROP } ]
[ TABLESPACE tablespace_name ]
postgres=# CREATE UNLOGGED TABLE tbl (col int);
CREATE TABLE
  • unlogged確認
postgres=# SELECT relname,relpersistence FROM pg_class WHERE relname='tbl';

 relname | relpersistence 
---------+----------------
 tbl     | u
(1 row)
  • ALTER TABLE文でLOGGED/UNLOGGED切り替え可能
postgres=# ALTER TABLE tbl SET LOGGED;
ALTER TABLE

postgres=# SELECT relname,relpersistence FROM pg_class WHERE relname='tbl';
 relname | relpersistence 
---------+----------------
 tbl     | p
(1 row)

Column: pg_resetwalコマンド

  • 公式
  • WALが壊れた時に実行するやつ
  • メジャーバージョンが揃っていること

walsender/walreceiverプロセスの設定方法

  • WALをやりとりするプロセス

    • walsender on primary
    • walreceiver on secondary
  • postgresql.confの設定項目

    • PostgreSQL11以前のrecovery.confでの設定項目も含まれる

      • PostgreSQL12ではrecovery.confがpostgresql.confに統合されたため
#------------------------------------------------------------------------------
# REPLICATION
#------------------------------------------------------------------------------

# - Sending Servers -

# Set these on the master and on any standby that will send replication data.

#max_wal_senders = 10		# max number of walsender processes
				# (change requires restart)

...

# - Master Server -

...

# - Standby Servers -

# These settings are ignored on a master server.

#primary_conninfo = ''			# connection string to sending server
					# (change requires restart)

...

#hot_standby = on			# "off" disallows queries during recovery
#------------------------------------------------------------------------------
# WRITE-AHEAD LOG
#------------------------------------------------------------------------------

# - Settings -

#wal_level = replica			# minimal, replica, or logical
					# (change requires restart)
...


# - Archiving -

#archive_mode = off		# enables archiving; off, on, or always
				# (change requires restart)
#archive_command = ''		# command to use to archive a logfile segment
				# placeholders: %p = path of file to archive
				#               %f = file name only
				# e.g. 'test ! -f /mnt/server/archivedir/%f && cp %p /mnt/server/archivedir/%f'

# - Archive Recovery -

# These are only used in recovery mode.

#restore_command = ''		# command to use to restore an archived logfile segment
				# placeholders: %p = path of file to restore
				#               %f = file name only
				# e.g. 'cp /mnt/server/archivedir/%f %p'
				# (change requires restart)
  • primary側設定項目
postgres=# SELECT name,setting,context,boot_val FROM pg_settings WHERE name IN ('wal_level', 'max_wal_senders');

      name       | setting |  context   | boot_val 
-----------------+---------+------------+----------
 max_wal_senders | 10      | postmaster | 10
 wal_level       | replica | postmaster | replica
(2 rows)
  • secondary側設定項目
postgres=# SELECT name,setting,context,boot_val FROM pg_settings WHERE name IN ('hot_standby', 'restore_command', 'primary_conninfo');

       name       | setting |  context   | boot_val 
------------------+---------+------------+----------
 hot_standby      | on      | postmaster | on
 primary_conninfo |         | postmaster | 
 restore_command  |         | postmaster | 
(3 rows)
  • primary側 pg_hba.conf 設定項目

    • DATABASE列にreplicationを指定
# Allow replication connections from localhost, by a user with the
# replication privilege.
local   replication     all                                     trust
host    replication     all             127.0.0.1/32            trust
host    replication     all             ::1/128                 trust

walsender/walreceiverプロセスの処理

  • secondary側のwalreceiver主導

    1. secondaryのwalreceiver起動、primaryへ接続要求
    2. primaryのwalsender起動
    3. secondaryのwalreceiverはprimaryのwalsenderへWALレコード要求
    4. primaryのwalsenderはsecondaryのwalreceiverへWALレコード送信
  • なので、max_wal_sendersの許す限り動的にsecondaryを増設できる

可能なレプリケーション構成

  • 1:Nのマルチスタンバイ構成

    • primary 1 : secondary N
  • PostgreSQL 9.2~: カスケード構成

    • スタンバイに対してさらにスタンバイ接続

同期/非同期の違い

  • clientがprimary serverのWAL処理を待つかどうか
  • primaryがsecondaryのWAL処理を待つかどうか
  • primaryのpostgresql.confsynchronous_commitで同期レベルを設定する
postgres=# SELECT name,setting,context,boot_val,enumvals FROM pg_settings WHERE name='synchronous_commit';

        name        | setting | context | boot_val |                 enumvals                 
--------------------+---------+---------+----------+------------------------------------------
 synchronous_commit | on      | user    | on       | {local,remote_write,remote_apply,on,off}
(1 row)
  • 同期レベル
設定値 (a)sync clientが
primary serverの
WAL処理を待つか
primaryが
secondaryの
WAL処理を待つか
off async 待たない 待たない
local async 待つ 待たない
remote_write sync 待つ 待つ(メモリ書き込みまで)
on sync 待つ 待つ(ディスク書き込みまで)
remote_apply sync 待つ 待つ(WAL適用まで)
  • 同期レベル最高のremote_apply以外では、WAL適用を待たないことに注意する

    • secondaryのデータはprimaryのデータと必ずしも同じ状態ではない
  • PostgreSQL 9.0でストリーミングレプリケーション搭載時点では非同期だけだった
  • PostgreSQL 9.1で同期レプリケーションも搭載

複数のスタンバイがある場合

  • どのスタンバイを「同期」として扱うか
  • secondaryのprimary_conninfoで、接続先情報に「ぼくの名前は○○です」という情報を添えておく
primary_conninfo = 'user=postgres port=5432 application_name=sby'
  • primaryのsynchronous_standby_namesで設定

    • reloadで設定反映
    • 【所感】せっかくwalreceiver主導でsecondaryを動的に追加できるアーキテクチャなのに、この設定を更新するたびにprimaryのrestartが必要だと台無しだからかな
postgres=# SELECT name,setting,context,boot_val FROM pg_settings WHERE name='synchronous_standby_names';

           name            | setting | context | boot_val 
---------------------------+---------+---------+----------
 synchronous_standby_names |         | sighup  | 
(1 row)
  • sby sby2 sby3 のうち接続できたもの先頭1つが同期」
synchronous_standby_names = 'sby,sby2,sby3'
  • バージョンによって「最初のN個」とか「どれかN個」とかも設定できる
  • synchronous_standby_namesが空白(デフォルト)だと、いずれも同期として扱わない