レプリケーション ── ストリーミング・ロジカル
このレイヤーの役割:データを複数のサーバーにコピーし、読み取りスケールアウト・高可用性・災害対策を実現する。

この章で何ができるようになるか:ストリーミングレプリケーションとロジカルレプリケーションの仕組みと使い分けを理解し、レプリケーション遅延の原因と対策を説明できるようになる。
2種類のレプリケーション
ストリーミングレプリケーション(物理レプリケーション)
WAL をそのままレプリカに転送して適用する。バイトレベルの完全コピー。
Primary → WAL → Replica(全く同じバイト列を持つ)
特徴:
✅ セットアップが簡単
✅ 全てのデータが自動的にレプリケートされる
✅ レプリカでの適用が高速(WAL を再生するだけ)
❌ PostgreSQL のメジャーバージョンが一致する必要
❌ テーブル単位の選択的レプリケーションはできない
❌ レプリカは完全に読み取り専用(書き込み不可)
-- Primary の設定(postgresql.conf)
wal_level = replica
max_wal_senders = 10
wal_keep_size = '1GB'
-- Replica の構築
pg_basebackup -h primary-host -D /var/lib/postgresql/data -P -R
-- -R: standby.signal ファイルと primary_conninfo を自動設定
-- Replica の postgresql.conf
primary_conninfo = 'host=primary-host port=5432 user=replicator'
hot_standby = on -- レプリカでの SELECT を許可
ロジカルレプリケーション(PostgreSQL 10+)
WAL を**論理的な変更(INSERT/UPDATE/DELETE)**にデコードして転送する。
Primary → WAL → Logical Decoder → INSERT/UPDATE/DELETE → Replica
特徴:
✅ テーブル単位で選択的にレプリケーション可能
✅ 異なる PostgreSQL バージョン間で動作
✅ レプリカに追加のインデックスやトリガーを設定可能
✅ レプリカに書き込み可能(双方向も原理上可能)
❌ DDL(ALTER TABLE 等)はレプリケーションされない
❌ 大きなテーブルの初期同期が遅い
❌ TRUNCATE は PostgreSQL 11+ でサポート
-- Primary: Publication の作成
CREATE PUBLICATION my_pub FOR TABLE users, orders;
-- または全テーブル:
CREATE PUBLICATION my_pub FOR ALL TABLES;
-- Replica: Subscription の作成
CREATE SUBSCRIPTION my_sub
CONNECTION 'host=primary-host port=5432 dbname=mydb user=replicator'
PUBLICATION my_pub;
-- → 初期データの同期が開始される
-- → その後はリアルタイムで変更が転送される
同期 vs 非同期レプリケーション
-- 非同期(デフォルト): Primary は WAL を書いたら即座にコミットを返す
synchronous_standby_names = ''
-- 同期: 少なくとも1台のレプリカが WAL を受信してからコミットを返す
synchronous_standby_names = 'FIRST 1 (replica1, replica2)'
-- → 書き込みレイテンシが増加するが、データロスのリスクがゼロ
-- 準同期: レプリカが WAL を受信(ディスクに書き込む前)でコミット
synchronous_commit = remote_write
-- → remote_apply(適用完了まで待つ)より速い
| 非同期 | 同期 | |
|---|---|---|
| 書き込みレイテンシ | 低い | 高い(ネットワーク RTT 分) |
| フェイルオーバー時のデータロス | わずかにあり得る | なし |
| レプリカ障害時の影響 | なし | Primary の書き込みがブロックされる |
レプリケーション遅延の監視と対策
-- Primary 側: レプリカの遅延を確認
SELECT
client_addr,
state,
sent_lsn,
write_lsn,
flush_lsn,
replay_lsn,
pg_wal_lsn_diff(sent_lsn, replay_lsn) AS replay_lag_bytes,
reply_time
FROM pg_stat_replication;
-- Replica 側: 自分の遅延を確認
SELECT
now() - pg_last_xact_replay_timestamp() AS replication_delay;
-- → 数秒以内なら正常。数分以上なら問題あり。
遅延の原因と対策
1. ネットワーク帯域不足
→ WAL の転送速度がネットワーク帯域を超えている
→ 対策: WAL 圧縮(wal_compression = on)
2. レプリカの I/O 性能不足
→ WAL の適用がディスク書き込みに律速
→ 対策: レプリカにも SSD を使う
3. レプリカの負荷(重い SELECT クエリ)
→ WAL 適用プロセスがリソースを取得できない
→ 対策: レプリカ上の重いクエリを制限
4. 長時間クエリとの競合
→ レプリカ上の SELECT が WAL 適用をブロック
→ hot_standby_feedback = on で VACUUM の遅延を通知
→ max_standby_streaming_delay = '30s'(30秒待ってからクエリをキャンセル)
フェイルオーバー
Primary が障害:
1. レプリカを新 Primary に昇格(promote)
2. アプリケーションの接続先を切り替え
手動昇格:
pg_ctl promote -D /var/lib/postgresql/data
自動フェイルオーバー:
Patroni(etcd ベース)
pgpool-II
repmgr
→ ヘルスチェック → 障害検知 → 自動昇格 → DNS/VIP 切り替え
まとめ
| レプリケーション方式 | 単位 | 用途 |
|---|---|---|
| ストリーミング(物理) | DB 全体 | 読み取りスケールアウト、HA |
| ロジカル | テーブル単位 | 選択的レプリケーション、異バージョン間 |
| 同期 | — | データロスゼロが必須の場合 |
| 非同期 | — | パフォーマンス優先 |