PostgreSQLエンタープライズ・コンソーシアム 技術部会 WG#3

WG3活動報告書
レプリケーション調査編

PostgreSQL Enterprise Consortium

目次
目次
1. 改訂履歴
2. ライセンス3. はじめに3.1. PostgreSQLエンタープライズコンソーシアムとWG3について
3.2. 本資料の概要と目的
3.3. 本資料の構成
3.4. 想定読者
3.5. 参考文献
4. PostgreSQLにおけるレプリケーション
4.1. レプリケーションの目的
4.1.1. 可用性の向上
4.1.2. 性能向上
4.1.2.1. 参照の同期
4.1.2.2. 更新による一貫性の維持
4.2. 代表的なレプリケーションの手法
4.2.1. シングルマスタ
4.2.1.1. ストレージレプリケーション
4.2.1.2. トリガベースレプリケーション
4.2.1.3. クエリベースレプリケーション
4.2.1.4. ストリーミングレプリケーション
4.2.1.5. ロジカルレプリケーション
4.2.2. マルチマスタ
4.2.2.1. Bi-Directional Replication
4.2.2.2. Bucardo
4.3. 参考文献
5. ストリーミングレプリケーション
5.1. はじめに
5.1.1. PostgreSQLのストリーミングレプリケーションの特徴
5.1.2. 検証を実施したSR構成
5.2. SR環境構築時の設定項目、推奨値
5.2.1. SRにおける目的別の設定
5.2.2. SRの基本設定手順（2ノード）
5.2.3. スレーブの追加
5.2.4. アーカイブモード運用
5.2.5. レプリケーションスロット
5.2.6. 【参考情報】同期モードにおけるパフォーマンスへの影響
5.2.7. 遅延レプリケーション
5.2.8. WAL圧縮
5.3. SR環境の監視
5.3.1. レプリケーション操作ログの監視
5.3.1.1. 調査の目的
5.3.1.2. 調査結果
5.3.1.3. 調査結果
5.3.2. 同期状況の監視
5.3.2.1. 同期遅延監視
5.3.3. 更新、読み取りの監視
5.3.3.1. 更新・読取監視
5.3.4. スプリットブレインの監視
5.3.4.1. スプリットブレインの定義と調査の目的
5.3.4.2. 監視方法
5.3.4.3. サーバログの監視
5.3.4.4. pg_controldataコマンドによる監視
5.3.4.5. pg_control_recoveryコマンドによる監視
5.3.4.6. pg_is_in_recovery関数による監視
5.4. SR環境の障害時運用
5.4.1. 本文書における用語の定義
Page 2 of 285

2
8
9
10
10
10
10
10
11
12
12
12
12
12
12
12
12
13
13
13
13
13
14
14
14
14
15
15
15
17
18
18
18
23
23
25
26
27
30
30
30
30
31
31
31
32
34
34
36
36
36
36
37
37
38
38
38

PostgreSQL Enterprise Consortium

5.4.1.1. フェイルオーバ
5.4.1.2. フェイルバック
5.4.1.3. スイッチオーバ
5.4.1.4. スイッチバック
5.4.2. 障害時運用手順（2ノード構成）
5.4.2.1. フェイルオーバ
5.4.2.2. フェイルバック
5.4.2.3. スイッチオーバ
5.4.2.4. スイッチバック
5.4.2.5. スレーブ障害による対処
5.4.3. 障害時運用手順（複数スレーブ）
5.4.3.1. フェイルオーバ(複数スレーブ )5.4.3.2. フェイルバック(複数スレーブ )5.4.3.3. スイッチオーバ(複数スレーブ )5.4.3.4. スイッチバック(複数スレーブ )5.4.3.5. スレーブ障害による対処(複数スレーブ )5.4.4. 障害時運用手順（カスケード構成）
5.4.4.1. フェイルオーバ
5.4.4.2. 同期スレーブへのフェイルオーバ
5.4.4.3. 非同期スレーブへのフェイルオーバ
5.4.4.4. フェイルバック
5.4.4.5. 旧マスタを同期モードスレーブとしてフェイルバック
5.4.4.6. 旧マスタをインスタンス障害からフェイルバック
5.4.4.7. 旧マスタ/旧スレーブ 1をフェイルバック
5.4.4.8. スイッチオーバ
5.4.4.9. 同期スレーブへのスイッチオーバ
5.4.4.10. 非同期スレーブへのスイッチオーバ
5.4.4.11. スイッチバック
5.4.4.12. 同期モードスレーブからマスタへのスイッチバック
5.4.4.13. 非同期モードスレーブからマスタへのスイッチバック
5.4.4.14. pg_rewindを使用したスイッチバック
5.4.4.15. スレーブ障害による対処
5.5. まとめ5.5.1. SR機能拡張の歴史
6. ロジカルレプリケーション
6.1. ロジカルレプリケーションの概要
6.1.1. 概要
6.1.2. 特徴
6.1.3. アーキテクチャ
6.1.4. ユースケース
6.1.5. 制限事項
6.2. ロジカルレプリケーションの設定
6.2.1. データベース単位の設定
6.2.1.1. 検証目的
6.2.1.2. 検証内容
6.2.1.3. 検証環境
6.2.1.4. 検証手順
6.2.2. テーブル単位の設定
6.2.2.1. 検証目的
6.2.2.2. 検証内容
6.2.2.3. 検証環境
6.2.2.4. 検証手順
6.2.3. レプリケーション対象とする更新処理の限定
6.2.3.1. 検証目的
6.2.3.2. 検証内容
6.2.3.3. 検証環境
6.2.3.4. 検証手順
6.2.4. 設定するパラメータ
6.2.4.1. 実行時パラメータ
Page 3 of 285

38
38
39
39
40
41
42
46
47
50
51
52
54
59
60
63
65
65
65
66
67
67
72
76
83
83
87
91
91
94
97
104
106
106
109
109
109
109
109
110
110
111
111
111
111
112
112
117
117
117
117
117
119
119
119
119
120
123
123

PostgreSQL Enterprise Consortium

6.2.4.2. 実行時パラメータと起動プロセスの関係
6.2.4.3. 考察
6.3. ロジカルレプリケーションの応用
6.3.1. primary keyが定義されていないテーブルのレプリケーション設定
6.3.1.1. 検証目的
6.3.1.2. 検証内容
6.3.1.3. 検証環境
6.3.1.4. 検証手順
6.3.2. 同期レプリケーション
6.3.2.1. 検証目的
6.3.2.2. 検証内容
6.3.2.3. 検証環境
6.3.2.4. 検証手順
6.3.3. 複数サブスクリプション
6.3.3.1. 検証目的
6.3.3.2. 検証内容
6.3.3.3. 検証環境
6.3.3.4. 検証手順
6.3.4. カスケード構成
6.3.4.1. 検証目的
6.3.4.2. 検証内容
6.3.4.3. 検証環境
6.3.4.4. 検証手順
6.3.5. パーティショニングとの組み合わせ
6.3.5.1. 検証構成
6.3.5.2. 組み合わせた場合の利点
6.3.5.3. 検証内容
6.3.5.4. 検証結果
6.4. ロジカルレプリケーションの運用
6.4.1. 監視
6.4.1.1. 前提とする環境
6.4.1.2. ロジカルレプリケーションの設定
6.4.1.2.1. Publisherで確認できる情報
6.4.1.2.2. Subscriberで確認できる情報
6.4.1.3. ロジカルレプリケーションの稼働状態
6.4.1.3.1. Publisherで確認できる情報
6.4.1.3.2. Subscriberで確認できる情報
6.4.2. 障害時の動作確認
6.4.2.1. 前提とする環境
6.4.2.2. 障害ケース1 ロジカルレプリケーション関連プロセスの異常終了
6.4.2.3. 障害ケース2 Publisherのノード停止
6.4.2.4. 障害ケース3 Subscriberのノード停止
6.4.2.5. 障害ケース4 Publisher/Subscriber間のネットワーク断絶
6.4.3. レプリケーション対象テーブルの追加/削除
6.4.3.1. 前提とする環境
6.4.3.2. レプリケーション対象のテーブルを追加する手順(データベース単位のPublication)
6.4.3.3. レプリケーション対象のテーブルを追加する手順(テーブル単位のPublication)
6.4.3.4. レプリケーション対象のテーブルを変更する手順
6.4.3.5. レプリケーション対象のテーブルにインデックスを追加する手順
6.4.4. レプリケーション対象外となる操作、オブジェクト
6.4.4.1. 前提とする環境
6.4.4.2. TRUNCATE
6.4.4.3. シーケンス
6.4.5. 更新競合時の動作
6.4.5.1. 前提とする環境
6.4.5.2. 更新時の競合発生と解消
6.4.5.2.1. 主キー違反/一意キー違反
6.4.5.2.2. CHECK制約違反
6.4.5.2.3. 更新データが存在しない
Page 4 of 285

124
129
130
130
130
130
130
131
134
134
135
135
135
141
141
141
141
142
150
150
150
150
151
156
156
156
156
156
169
169
169
170
170
172
174
174
177
178
178
179
185
187
189
191
191
192
194
195
197
198
199
200
201
202
203
204
204
205
206

PostgreSQL Enterprise Consortium

6.4.5.2.4. 削除データが存在しない
6.4.5.2.5. テーブルが存在しない
6.4.5.2.6. 一部の列が存在しない
6.4.5.2.7. データ型変換エラー
6.4.5.2.8. テーブルのロック
6.4.5.2.9. 更新対象レコードのロック
6.4.5.3. 初期データ同期時の競合発生と解消
6.4.5.3.1. 主キー違反/一意キー違反
6.4.5.3.2. CHECK制約違反
6.4.5.3.3. 更新データが存在しない
6.4.5.3.4. 削除データが存在しない
6.4.5.3.5. テーブルが存在しない
6.4.5.3.6. 一部の列が存在しない
6.4.5.3.7. データ型変換エラー
6.4.5.3.8. テーブルのロック
6.4.5.3.9. 更新対象レコードのロック
6.4.5.4. WALのスキップによる競合の解消
6.4.6. ストリーミングレプリケーションとの併用
6.4.6.1. 前提とする環境
6.4.6.2. ストリーミングレプリケーションを併用する環境の構築手順
6.4.6.3. ストリーミングレプリケーションを併用する環境で障害が発生した場合の運用手順
6.4.6.4. 考察
6.5. まとめ
7. Bi-Directional Replication (BDR)
7.1. はじめに
7.1.1. BDRの特徴
7.1.1.1. BDRとは
7.1.1.2. ユースケース
7.1.1.3. メカニズム
7.1.1.4. 整合性
7.1.1.5. シーケンス
7.1.1.6. 比較表
7.1.1.7. サポート
7.2. 調査、検証の目的
7.3. 調査、検証の前提
7.4. BDR環境構築時の設定と設定手順
7.4.1. BDR環境の設定手順
7.4.1.1. 概要
7.4.1.2. 対象試験サーバ
7.4.1.3. 事前確認
7.5. BDR動作検証
7.5.1. ノードの追加・切り離し
7.5.1.1. 検証の目的
7.5.1.2. 検証内容
7.5.1.3. 検証環境
7.5.1.4. 検証手順
7.5.1.4.1. ノード切り離し
7.5.1.4.2. ノード追加
7.5.1.5. 検証結果
7.5.2. グローバルシーケンス設定
7.5.2.1. 検証の目的
7.5.2.2. 検証環境
7.5.2.3. 検証手順
7.5.2.4. 検証結果
7.5.3. 選択的レプリケーション
7.5.3.1. 検証目的
7.5.3.2. 検証内容
7.5.3.3. 検証環境
7.5.3.4. 検証手順
Page 5 of 285

207
207
209
211
212
213
214
214
215
215
215
215
216
217
217
218
218
221
221
222
222
230
231
232
232
232
232
232
232
233
233
234
234
234
235
235
235
235
235
236
241
241
241
241
241
241
241
243
247
247
247
247
247
249
250
250
250
250
250

PostgreSQL Enterprise Consortium

7.5.3.5. 検証結果
7.5.4. 更新処理競合時の動作
7.5.4.1. 検証目的
7.5.4.2. 検証内容
7.5.4.3. 検証環境
7.5.4.4. 検証手順
7.5.4.4.1. PRIMARY KEYまたはUNIQUE制約
7.5.4.4.2. 外部キー制約
7.5.4.4.3. 排他制約
7.5.4.4.4. グローバルなデータ
7.5.4.4.5. ロックの競合とデッドロックの中断
7.5.4.4.6. その他
7.5.4.5. 検証結果
7.5.5. ノード障害と復旧
7.5.5.1. 検証の目的
7.5.5.2. 検証内容
7.5.5.3. 検証環境
7.5.5.4. 検証手順
7.5.5.4.1. ノード障害
7.5.5.4.2. ノード復旧
7.5.5.5. 検証結果
7.5.5.6. 備考
7.6. BDR性能検証
7.6.1. 更新性能検証結果
7.6.1.1. 検証目的
7.6.1.1.1. 検証内容
7.6.1.1.2. 検証環境
7.6.1.1.3. 検証手順
7.6.1.1.4. 検証結果
7.7. まとめ7.7.1. BDR検証まとめ
7.8. 参考文献
7.8.1. 関数一覧
7.8.2. システムカタログ一覧
8. まとめ
9. 著者

Page 6 of 285

254
254
254
254
255
255
256
264
265
268
269
270
272
272
272
272
272
272
272
275
278
278
278
278
278
278
279
279
281
282
282
282
282
283
284
285

PostgreSQL Enterprise Consortium

Page 7 of 285

PostgreSQL Enterprise Consortium

1. 改訂履歴
版

改訂日

変更内容

1.0

2017/06/20 2016 年度版として新規作成- Po stgreSQ L 9.6 を対象バージョンとする

2.0

2018/04/11 2017 年度版として主に以下の章を加筆修正- 5 章 ストリーミングレプリケーション
Po stgreSQ L 10 に対応
複数スレーブ対応 (1.0 版では2 ノードに限定していた )- 6 章 ロジカルレプリケーションの追加
Po stgreSQ L 10 の新機能として追加

Page 8 of 285

PostgreSQL Enterprise Consortium

2. ライセンス
本作品はCC-B Yライセンスによって許諾されています。 ライセンスの内容を知りたい方は こちら でご確認ください。 文書の内容、表記に関する誤り、ご要望、感想等につきましては、PG ECo n s のサイト を通じてお寄せいただきますようお願いいたします。

Eclip se は、Eclip se Fo u n datio n In c の米国、およびその他の国における商標もしくは登録商標です。
IB M およびDB 2 は、世界の多くの国で登録された In tern atio n al B u sin ess Mach in es Co rp o ratio n の商標です。
In tel 、インテルおよびXeo n は、米国およびその他の国における In tel Co rp o ratio n の商標です。
Java は、O racle Co rp o ratio n 及びその子会社、関連会社の米国及びその他の国における登録商標です。 文中の社名、商品名等は各社の商標または登録商標である場合があります。
Lin u x は、Lin u s To rvalds 氏の日本およびその他の国における登録商標または商標です。
Red Hat およびSh ado w man lo go は、米国およびその他の国におけるRed Hat,In c. の商標または登録商標です。
Micro so ft 、W in do w s Server 、SQ L Server 、米国 Micro so ft Co rp o ratio n の米国及びその他の国における登録商標または商標です。
MySQ L は、O racle Co rp o ratio n 及びその子会社、関連会社の米国及びその他の国における登録商標です。 文中の社名、商品名等は各社の商標または登録商標である場合があります。
O racle は、O racle Co rp o ratio n 及びその子会社、関連会社の米国及びその他の国における登録商標です。 文中の社名、商品名等は各社の商標または登録商標である場合があります。
Po stgreSQ L は、Po stgreSQ L Co mmu n ity Asso ciatio n o f Can ada のカナダにおける登録商標およびその他の国における商標です。
W in do w s は米国 Micro so ft Co rp o ratio n の米国およびその他の国における登録商標です。
TPC, TPC B en ch mark, TPC-B , TPC-C, TPC-E, tp mC, TPC-H, TPC-DS, Q p h H は米国Tran sactio n Pro cessin g Perfo rman ce Co u n cil の商標です。
その他、本資料に記載されている社名及び商品名はそれぞれ各社が 商標または登録商標として使用している場合があります 。

Page 9 of 285

PostgreSQL Enterprise Consortium

3. はじめに3.1. PostgreSQLエンタープライズコンソーシアムとWG3について
Po stgreSQ L エンタープライズコンソーシアム(略称 PG ECo n s)は、Po stgreSQ L 本体および各種ツールの情報収集と提供、整備などの活動を通じて、ミッションクリティカル性の高いエンタープライズ領域へのPo stgreSQ L の普及を推進することを目的として設立された団体です。
PG ECo n s 技術部会ではPo stgreSQ L の普及に資する課題を活動テーマとし、3 つのワーキンググループで具体的な活動を行っています。
W G 1 （新技術検証ワーキンググループ）
W G 2 （移行ワーキンググループ）
W G 3 （課題検討ワーキンググループ）
これら 3 つのワーキンググループのうち、W G 1 、W G 3 については 2015 年度まではそれぞれ、「性能ワーキンググループ」、「設計運用ワーキンググループ」という名称で活動してきました。2016 年度は、従来の活動領域を広げる意図のもとでそれらを再定義し、上記のような名称に改めました。
これに伴い、W G 3 ではPo stgreSQ L の設計運用を中心としたさまざまな課題の解決のための調査検証を行い、Po stgreSQ L が広く活用される事を推進していくこととしました。

3.2. 本資料の概要と目的
本資料はW G 3 の2017 年度の活動として Po stgreSQ L におけるレプリケーション機能について、調査検討した結果をまとめたものです。
これまでにも、W G 3 では3 度にわたってレプリケーションについて取り上げています。

2013 年では、Po stgreSQ L をエンタープライズ領域で活用するにあたって、業務要件とコストとのバランスを考えて可能な限り業務を継続できるように、システムの運用や保守サービスをPo stgreSQ L で実現する手法についてまとめました [PG ECo n s_W G 3_2013] 。報告書では、可用性を向上させるための手段としてレプリケーションを取り上げています。今年度の報告書では取り上げなかった、ストレージレプリケーション(DRB D)、トリガベースレプリケーション(Slo n y-I)についても紹介しています。
2014 年度では、可用性のうち「災害対策」に焦点を当て、ITサービス継続を可能とする Po stgreSQ L の構成について調査検討しています [PG ECo n s_W G 3_2014] 。
ストリーミングレプリケーションを含む代表的な Po stgreSQ L のシステム構成を挙げて、データベースのデータ保全性、サービス継続性の観点から各構成の得失を示しました。

2016 年度では、ストリーミングレプリケーションの2 ノードにおける運用ノウハウの整理や Po stgreSQ L をベースとして双方向レプリケーションがリリースされています。
これらの開発動向と過去の検討結果とを踏まえて、2017 年度は Po stgreSQ L 10 にて機能拡充が進むストリーミングレプリケーションの3 ノードにおける運用ノウハウの整
理と、新機能のロジカルレプリケーションの調査を実施しました。

3.3. 本資料の構成
はじめに

Po stgreSQ L におけるレプリケーション
レプリケーションの目的
代表的なレプリケーションの手法
ストリーミングレプリケーション
はじめに
SR 環境構築時の設定項目、推奨値
SR 環境の監視
SR 環境の障害時運用
スレーブのアーカイブ保存
まとめ
ロジカルレプリケーション（仮）はじめにロジカルレプリケーション環境構築時の設定項目、推奨値
ロジカルレプリケーション動作検証
ロジカルレプリケーション性能検証
まとめ

B i-Directio n al Rep licatio n (B DR)
はじめに
B DR 環境構築時の設定項目、推奨値
B DR 動作検証
B DR 性能検証
まとめ
おわりに

3.4. 想定読者
Page 10 of 285

PostgreSQL Enterprise Consortium
本資料の読者は以下のような知識を有していることを想定しています。

DB MS を操作してデータベースの構築、保守、運用を行う DB A の知識
Po stgreSQ L を利用する上での基礎的な知識

3.5. 参考文献[PG ECo n s_W G 3_2013] Po stgreSQ L En terp rise Co n so rtiu m. 2013 年度W G 3 活動成果報告書. 2014. h ttp s://w w w .p geco n s.o rg/w p co n ten t/u p lo ads/PG ECo n s/2013/W G 3/p geco n s-w g3-2013-rep o rt.p df[PG ECo n s_W G 3_2014] Po stgreSQ L En terp rise Co n so rtiu m. 2014 年度W G 3 活動成果報告書 (可用性編). 2014. h ttp s://w w w .p geco n s.o rg/w p co n ten t/u p lo ads/PG ECo n s/2014/W G 3/PG ECo n s_2014_W G 3_Availab ility.p df[Po stgresB DR] 2n dQ u adran t. Po stgres-B DR. Versio n Po stgres-B DR94 1.0.2, No vemb er 14, 2016. h ttp s://2n dqu adran t.co m/en u s/reso u rces/b dr/

Page 11 of 285

PostgreSQL Enterprise Consortium

4. PostgreSQLにおけるレプリケーション
データベースにおいて、レプリケーションとは複数のデータベースサーバの間で、 何らかの一貫性を保ちながら、その内容を複製する手法を指します。Po stgreSQ L においては、 9.0 以降の各バージョンの基本機能として、レプリケーションが実現されています。 また、Po stgreSQ L に付加するツールによってもレプリケーションが実現されています。
この章では、各種のレプリケーション機能を目的に応じて使い分けるために、機能と特性について簡単に紹介します。

4.1. レプリケーションの目的
レプリケーションによって複数のデータベースサーバの複製を作ることによって、シングルサーバでは対応が難しい要件にも対応できるようになります。それらは、データの冗長性(複製があること)と複数サーバによる処理の分散の結果です。

4.1.1. 可用性の 向上
可用性はITシステムの非機能要件の一つで、システムを継続的に利用可能とすることです [IPA] 。 可用性は「継続性」「耐障害性」「災害対策」「回復性」という 4 つの要素から構成されますが、レプリケーションによってデータベースサーバを冗長化することで、1 つのサーバで故障や災害が生じたときにもレプリカが格納されている残りのサーバ
でサービスを継続することができるるようになります。また、運用上の停止が必要な場合であっても、各サーバを順次停止して作業することで、サービス全体としては停止させないようにできます。
先に挙げた可用性の4 つの要素をどの程度満足するかはデータベースの構成によって変わってきます。詳しくは『 2013 年度W G 3 活動成果報告書』[PG ECo n s_W G 3_2013] を参考にしてください。

4.1.2. 性能向上
レプリケーションによって、同じ情報を格納しているデータベースサーバが複数存在することになります。それらのサーバでアプリケーションからの要求にこたえることが出来れば、システム全体としての性能状況が期待できます(スケールアウト)。アプリケーションからの要求を複数のサーバに分散させる際には、更新(削除・挿入を含む)クエリを特定の1 サーバに集約する「シングルマスタ」構成と、複数のサーバに分散する「マルチマスタ」構成があります。また、参照クエリを複数のサーバに分散させることを参照負荷分散と呼びます。
レプリケーションクラスタを性能向上に用いる場合、複数のデータをベースを同時に運用することから生じる特有の課題があります。レプリケーション方式を選択する際には、それらの課題をどの程度解決しているのかについても考慮する必要があります。

4.1.2.1. 参照の同期
ある瞬間に同一の参照クエリを異なるサーバに送った時に、まったく同じ結果が返ってくるものと、そうでないものとがあります。同じ結果が得られる場合、サーバは同期している、同期レプリケーションであると言います。

4.1.2.2. 更新に よる一貫性の維持
マルチマスタ構成の場合には同期の問題に加えて、更新の衝突と一貫性の維持が問題となります。
更新の衝突
複数のアプリケーションプログラムから同一の更新操作を実行しようとした場合、単一のDB サーバであればどちらかの操作が遅延され、場合によってはエラーとなります。マルチマスタ構成では、レプリケーションの送信側でなされた更新と、そのサーバに直接要求された更新とが衝突した場合に、どちらが優先するのかが問題となります。
一貫性の維持更新が衝突した結果、レプリケーションの送信側と受信側でデータベースの内容が異なってしまうと、レプリケーションによって構成されるクラスタ全体でデータベースの一貫性が維持されなくなります。この問題を適切に対処する必要があります。

4.2. 代表的なレプリケーションの手法
ここでは、本報告書で取り上げるレプリケーション手法を中心に、Po stgreSQ L で利用できる代表的なレプリケーション手法を紹介します。レプリケーションを利用する立場
からは、シングルマスタとマルチマスタに二分することができます。その上で、レプリカを生成する方法に着目して代表例を挙げ、そのメリット・デメリットを説明します。
なお、『 2013 年度W G 3 活動成果報告書』 [PG ECo n s_W G 3_2013] では可用性向上の観点から、レプリケーションを含めて様々な Po stgreSQ L の構成を取り上げていますので、併せてご覧ください。
コミュニティのW iki ページには、Po stgreSQ L 上で動作するクラスタソフトウェアについての解説があり、その中にレプリケーションも含まれています [PG W iki_rep lica] 。ここで紹介する紹介するレプリケーションソフトウェアについても紹介されています。

4.2.1. シ ン グル マスタ
シングルマスタ構成の場合、レプリカを生成する手法には以下のようなものがあります。
ストレージレプリケーション
トリガベースレプリケーション
クエリベースレプリケーション
ストリーミングレプリケーション

Page 12 of 285

PostgreSQL Enterprise Consortium
ロジカルレプリケーション

4.2.1.1. スト レージレプリケ ーショ ン
Po stgreSQ L やその上で動作するツールを介することなく、データを格納するストレージのレベルでデータを複製します。ストレージ装置自体がレプリカを生成するものや、
DRB D [DRB D] のように Lin u x 上で動作するソフトウェアによる実現があります。
メリット

Po stgreSQ L からは単一のサーバに見えるので、単一サーバと同じように運用できます
デメリット
受信側のサーバはデータベースとしては動作していないので、負荷分散に利用することができません

4.2.1.2. ト リガ ベースレプリケ ーショ ン
Po stgreSQ L のデータベース内に更新によって起動されるトリガを設定しておき、更新による変分を受信側のサーバに送り出すもの。代表的な製品に Slo n y-I があります。
以下では Slo n y-I での主なメリット・デメリットを紹介します。
メリット

Po stgreSQ L のデータベースクラスタに含まれる表全体だけでなく、任意のテーブルについてだけ複製を作成することができる
更新される表については参照負荷分散、それ以外の表については更新負荷分散が可能です
デメリット
比較的オーバヘッドが大きいため、後述のストリーミングレプリケーション方式に比べて、性能が低い傾向があります [p glo gical]

4.2.1.3. ク エリベースレプリケ ーショ ン
アプリケーションプログラムとDB サーバ(Po stgreSQ L)の間に入るミドルウェアによって、発行されたクエリを複製して複数のDB サーバに送信することで、データベースを複製します。代表的な製品に Pgp o o l-II [Pgp o o l-II] があります。
メリット
複数のDB サーバを用いて負荷分散を実現する際に、参照クエリ・更新クエリともに適切なサーバにクエリが自動的に振り分けられるため、アプリケーショ
ンから見ると単一のDB サーバを利用しているように見える
デメリット
一部のSQ L 文に対する挙動が単一のDB サーバとは異なる

4.2.1.4. スト リーミ ングレプリケ ーショ ン
Po stgreSQ L データベースでは、更新をコミットした際にその結果をクラッシュ等で失わないように更新情報をファイルに書きこむログ先行書込み(W rite Ah ead Lo ggin g;
W AL)を用いています。このW AL ファイルにはデータベースに対する更新を全て復元することができる情報が含まれていますから、これを他のDB サーバに転送することでデータベースを複製することができる --- これがストリーミングレプリケーションの基本的な考え方です。
ストリーミングレプリケーションは、W AL ファイルに書かれた内容をほぼそのまま受信側(スレーブサーバ)に送り出すことで、送信側(マスタサーバ)と物理的に一致するDB を複製
します。
メリット
受信側サーバに送信側サーバと物理的に一致したデータベースを複製することができる
送信側サーバでコミット済みのデータを受信側で確実に書き込み済みにすることができるため、高信頼化に適している
参照負荷分散ができる
デメリット
特定のデータベース、表だけを複製することはできない
メジャーバージョンが異なるPo stgreSQ L の間では利用できない
受信側サーバのデータベースは更新できない

4.2.1.5. ロ ジカ ルレプリケ ーショ ン
ロジカルレプリケーションは、Po stgreSQ L バージョン10 から標準採用された機能です。ストリーミングレプリケーションが送信側のW AL をそのまま受信側に転送するのに対して、ロジカルレプリケーションは送信側でW AL ファイルをデコードし、必要な変更内容のみを受信側に送ります。
メリット
送信側サーバの一部の表に対する更新だけを受信側に送ることができる
複数の送信側サーバの出力を1 つの受信側サーバで受け取ることができる

Page 13 of 285

PostgreSQL Enterprise Consortium
メジャーバージョンが異なるPo stgreSQ L の間でも利用できる
受信側サーバのデータベースを更新することができる
デメリット
レプリケーションできないSQ L やオブジェクトがあり、送信側と受信側で不整合が発生しないよう注意して運用する必要がある
受信側サーバのデータベースを更新した場合、送信側サーバでの更新内容と競合する可能性がある

4.2.2. マル チマスタ
マルチマスタ構成の場合、レプリカを生成する手法には以下のようなものがあります。

B i-Directio n al Rep licatio n
B u cardo
4.2.2.1. Bi-Directional Replication
2n d Q u adran t 社が公開している B i-Directio n al Rep licatio n は、先に紹介した論理レプリケーションを用いてデータを複製しつつ、複数のサーバでデータの更新を可能としたものです。主な用途としては地理的に離れた場所にある複数のサーバ間で、データを共有する利用形態を想定しています [Po stgresB DR] 。
メリット
論理レプリケーションを利用しているため、他の方式によるマルチマスタに比べてオーバヘッドが小さい
デメリット
レプリケーション自体は非同期に行われるため、複数サーバに同時に発行した参照クエリの結果が異なることがあります。

4.2.2.2. Bucardo
B u cardo は、トリガベースレプリケーションを使ってデータを複製します [B u cardo ] 。そのため、メリット、デメリットはシングルマスタのトリガベースレプリケーションと同じです。

4.3. 参考文献[IPA] 独立行政法人情報処理推進機構. 非機能要求グレード 利用ガイド[活用編]. 2010. h ttp ://w w w .ip a.go .jp /files/000026853.p df[PG W iki_rep lica] Smith , G .; G rittn er, K.; Pin o , Co n rad T.; Rin ger, C.; Simo n , R. et al. Rep licatio n , Clu sterin g, an d Co n n ectio n Po o lin g. 2017.
h ttp s://w iki.p o stgresql.o rg/w iki/Rep licatio n ,_Clu sterin g,_an d_Co n n ectio n _Po o lin g[DRB D] LINB IT, In c. h ttp ://w w w .drb d.o rg/en /[p glo gical] 2n dQ u adran t. p glo gical. h ttp s://2n dqu adran t.co m/en /reso u rces/p glo gical/[Pgp o o l-II] PgPo o l G lo b al Develo p men t G ro u p . h ttp ://w w w .p gp o o l.n et/mediaw iki/in dex.p h p /Main _Page[B u cardo ] Jen sen , G .; Sab in o , G . M. et al. h ttp s://b u cardo .o rg/w iki/Main _Page

Page 14 of 285

PostgreSQL Enterprise Consortium

5. ストリーミングレプリケーション
5.1. はじめに
5.1.1. PostgreSQL の ストリ ー ミン グレプリ ケー シ ョ ン の 特徴
Po stgreSQ L のストリーミングレプリケーション(以下、SR 構成）は以下を目的とした構成です。
可用性の向上性能向上
以下の特徴があります。
変更履歴が格納された W AL を操作単位でマスタ側からスレーブ側へ転送することでデータを同期

SR 基本構成図（スレーブ１台）
Po stgreSQ L 10 における主な機能を示します。
完全同期方式 により、スレーブ活用度の向上が期待できる同期式はW AL の転送までの保証であり、データ保護が目的。
完全同期式はW AL の適用までの保証であり、データ同期が保証されるため、リアルタイムのデータ参照に期待。
syn ch ro n o u s_co mmit には以下の設定が可能であり、要件に応じて選択。
remo te_ap p ly : 完全同期。W AL 適用まで保証。昇格時の時間短縮と参照負荷分散の拡充が目的。
on: 同期。W AL 転送（ディスク書き込み）まで保証。データ保護が目的。
remo te_w rite : 準同期。W AL 転送（メモリ書き込み）まで保証。データ保護とパフォーマンスのバランスが目的。
lo cal: 非同期。ローカルのW AL 書き込みまで保証。パフォーマンス優先が目的。
o ff: 完全非同期。ローカルのW AL 書き込みすら保証しない。最も高パフォーマンスだが非現実的。
以下の図はsyn ch ro n o u s_co mmit の設定による保証時点を示す。

レプリケ ーショ ンスロ ット により、スレーブに必要なW AL をマスタが確保し続ける
スレーブの障害時（ネットワーク不調含む）等にマスタでW AL を保持し切れず、W AL ファイルの再利用によりロストする懸念がある。
レプリケーションスロットの登場前はw al_keep _segmen ts パラメータによる調整、またはアーカイブモード運用が必要であった - w al_keep _segmen ts
はW AL 数を指定するものであるため、見積もりが困難である。 - アーカイブモードも複数スレーブ構成における不要W AL ファイルの判断はやはり困難。
レプリケーションスロットによりW AL ファイルの要・不要の判断をシステムに任せる事ができる。
SR 構成のW AL ファイル管理のためにアーカイブモード運用は必要はなく、過去の時点に復旧する(PITR)要件がある場合に設定する。

Page 15 of 285

PostgreSQL Enterprise Consortium
複数スレーブの場合は、スレーブ毎に専用のレプリケーションスロットを作成する。

複数同期スレーブ構成 が可能
従来も複数スレーブ構成は可能であったが、同期スレーブはその内の1 台までという制限があったがそれが取り払われた。
同期スレーブを任意(ANY)に選択するQ u o ru m-b ased 同期レプリケーションが実装された。

遅延レプリケ ーショ ン機能 により、オペレーションミスを反映させない事が可能にスレーブでの適用を一定時間待機する機能である。マスタでのオペミスが即座にスレーブに伝搬されるのを防ぐのが主な目的。
W AL ファイルの転送は遅延なく処理されるため、データ保全(RPO )の観点では問題ない。同期モードで設定する事もできる。
reco very.co n f のreco very_min _ap p ly_delayパラメータに遅延時間を設定。
従来からの手法として、PITR を使用してオペミス直前時点を指定してリカバリすることで障害回復を図る方法もある。

巻き戻し 機能 (p g_rew in d)により、効率の良いSR 再構成が可能に
旧マスタを分岐時点へ巻き戻す事で、新マスタに追い付く事が可能な状態とする機能である。
巻き戻し後、旧マスタに新マスタのW AL を適用（追い付き）する事で旧マスタを新スレーブとして構成。
従来はp g_b aseb acku p 等による再作成が必要であった。
大規模データベースでは、大幅な時間短縮が期待できる。
下図に巻き戻し機能のイメージを示す。

Page 16 of 285

PostgreSQL Enterprise Consortium

WAL 圧縮機能 により、W AL 転送の効率を改善
W AL 圧縮でサイズを縮小することにより、W AL 転送の効率改善が目的。
スレーブを遠隔地に配置する場合に特に有効。
注意点としては、圧縮（マスタ）や解凍（スレーブ）の負荷が発生する。

SR 構成の運用上の注意点
死活監視と障害発生時のフェイルオーバはPo stgreSQ L の機能ではできないため、クラスタソフトを利用する必要があります。 商用クラスタソフトが使用される場
合もありますが、オープンソースソフトウェアのPgp o o l-II と呼ばれるクラスタソフトを使用した例も多く報告されています。 特に参照負荷分散を行う場合は、
Pgp o o l-II を使用します。

5.1.2. 検証を実施した SR構成
以下のSR 構成にて検証を実施しています。
対象バージョンは Po stgreSQ L 102017 年11 月 時点の最新版
3 ノード構成
1. 複数スレーブ方式（親 - 子1 , 子2 ） スレーブ2 はマスタと繋がっている。

2. カスケード方式（親 - 子 - 孫） スレーブ2 はスレーブ1 と繋がっている

Po stgreSQ L の機能に限定
Pgp o o l-II 等のクラスタソフトに関するテーマは対象外

Page 17 of 285

PostgreSQL Enterprise Consortium

5.2. SR環境構築時の設定項目、推奨値
5.2.1. SRにお け る目的別の 設定
Po stgreSQ L のSR 構成には様々な機能があり、 対処する障害に応じて適切に設定する必要があります。 また各機能は組み合わせることが可能です。
"ストリーミングレプリケーション"を"SR"と表記します。
"レプリケーションスロット"を"スロット"と表記します。
表 5.1 SR の機能
機能

内容・ 目的

アーカイブW AL

注意点・ 補足

必要なW AL ファイルの保持。以下の2 種類。
物理バックアップ(PITR)に必要な（フルバックアップ以降の） W AL 。 障害発生直前まで復旧や操作ミス時に過去に戻す機能がある。
SR 構成において未転送のW AL を保持する。

レプリケーションスロッ
ト

スレーブに必要なW AL ファイルを保持する事によるSR 構成の維持。
対応する各スレーブの未転送のW AL を保持する。

遅延レプリケーション

スレーブ側のW AL 適用を意図して遅らせることで、スレーブを一定の過去の状態に維持する。 操作ミス対策に任意の時点まで巻き戻せるようにする。

W AL 圧縮

Fu ll Page W rite 時(チェックポイント後の最初の更新時)に、W AL
ファイルに書き出すフルページイメージを圧縮する。 圧縮された
W AL は適用時に解凍される。 W AL サイズを大幅に圧縮できる可能性があり、転送負荷の低減が期待される。特にディザスタ・リカバ
リ構成で有効と考えられる。

アーカイブW AL がディスク領域を圧迫しないように、不要となったアーカイブW AL の明示的な削除が必要。
SR 用途の場合、W AL ファイルの要不要の判断が困難であるため、現在では後述のレプリケーションスロットの使用が一般的。
SR 構成には物理バックアップという側面もあるため、過去に戻す
要件がなければ物理バックアップを取得する必要性は低い。
これらを考慮し、一般的には非アーカイブモード運用でも十分と考えられる。

スレーブへW AL ファイルの転送ができない状態が続くと、W AL ファ
イルが溜まり領域を圧迫する。
複数スレーブ構成では、スレーブ毎にスロットの使用/未使用を選択できる。
スレーブ毎にスロットを作成する必要がある。

スレーブは常に過去の状態であるため、参照活用には一定の制限がある。
フェイルオーバ時には遅延分の適用が必要になるため、RTO の点で難がある。そのため複数スレーブ構成の場合に 1 スレーブで構成するのが一般的。

マスタの圧縮時とスレーブの解凍時に通常より余分に CPU 負荷と時間がかる。

5.2.2. SRの 基本設定手順（ 2ノ ー ド）
基本的なSR 環境の設定手順を紹介します。 尚、マスタとスレーブの両サーバに Po stgreSQ L はインストール済みであり、 マスタ側ではデータベースクラスタを構築していることを前提としています。

1. 関連パラメータ
SR 構成に最低限必要な設定は以下の通りです。 スレーブはマスタのベースバックアップから作成されるため、マスタに設定したパラメータは全てスレーブも同様の値に設定されます。

"ストリーミングレプリケーション"を"SR"と表記します。
"レプリケーションスロット"を"スロット"と表記します。
説明の便宜上、以下の設定とします。
表 5.2 構成データ
項目

内容

マスタのIP アドレス およびサーバ名

192.168.100.101/24 server1

スレーブのIP アドレスおよびサーバ名

192.168.100.102/24 server2

ポート番号

5432

レプリケーション用ユーザ/パスワード

rep u ser/p assw o rd

Page 18 of 285

PostgreSQL Enterprise Consortium

表 5.3 SR 構成の設定(マスタのp g_h b a.co n f)
設定値

内容

h o st rep licatio n rep u ser 192.168.100.101/32 tru st
h o st rep licatio n rep u ser 192.168.100.102/32 tru st
またはCIDR 指定にて
h o st rep licatio n rep u ser 192.168.102.0/24 tru st

SR 用ユーザの接続を許可する。
・ p g_h b a.co n f は両ノード共通の設定となる事から、どちらがマスタになっても使用できる設定とする。
・ IP アドレスはW AL 転送を行うネットワーク LAN を指定。
パブリック LAN 以外に、ハートビート系LAN も有力。
・認証方式は任意だが、p assw o rd やmd5 ではパスワードファイルにより入力を求められないようにする。

表 5.4 SR の設定(マスタのp o stgresql.co n f)
パラ メ ータ

設定値

内容

listen _addresses'0.0.0.0'

接続を受け入れるIP を指定。
再起動で反映。

w al_level

rep lica

W AL に書かれる情報量を指定。
SR 構成の場合は rep lica に設定。
デフォルトがrep lica であるため変更不要。
再起動で反映。

w al_keep _segmen ts

64

暫定値として、max_w al_size/16MB の値を設定。
スロットを使用する場合は設定不要。
リロードで反映。

syn ch ro n o u s_co mmit

on

目的に応じて設定する。

remo te_ap p ly : 完全同期。W AL 適用までを保証。
o n : 同期。W AL 転送後のディスクへの書き込みまで保証。
remo te_w rite : 準同期。W AL 転送後のメモリ書き込みまで保証。
lo cal : 非同期。ローカルへの書き込みまで保証（リモートは保証しない）。
o ff : 完全非同期。ローカルへの書き込みも保証しない。
同期式におけるスレーブ障害時および復旧時の同期/非同期の切り替えは
syn ch ro n o u s_stan db y_n ames にて行う。本パラメータは固定。
リロードで反映。

syn ch ro n o u s_stan db y_n ames '' （非同期）

SR の非同期と同期を切り替えに使用。
非同期の場合 : ''同期の場合 : '*' あるいは特定スレーブのap p licatio n _n ame
リロードで反映。

max_w al_sen ders

10

起動するw al sen ders 数を指定。
p g_b aseb acku p でW AL ストリーミングを指定する場合はその分も考慮
通常はデフォルトの10 で問題ない。
再起動で反映。

max_rep licatio n _slo t

10

レプリケーションスロットを作成可能な最大数
通常はデフォルトの10 で問題ない。
再起動で反映。

restart_after_crasho ff

o n の場合、インスタンス障害(p o stgres プロセスは残存)の場合に、自動的に再起動を行う。
シングル構成では便利な機能であるが、SR 構成では管理が複雑化するため、o ff に設定するのが一般的。
リロードで反映。

Page 19 of 285

PostgreSQL Enterprise Consortium
表 5.5 SR の設定(スレーブのp o stgresql.co n f)
パラ メ ータ

設定値

内容

h o t_stan db yon

ホットスタンバイとして参照可能な状態で起動する。
スレーブに対しても監視SQ L を実行できるように通常は有効化する。
デフォルトでo n であるため、変更は不要。

h o t_stan db y_feedb ack

ono n の場合、スレーブが現在処理している問い合わせについて、マスタへフィー
ドバックを送る。
通常は有効化する。特にスロットを作成する場合には有効化が必須。
デフォルトはo ff であるため、変更が必要。

表 5.6 SR の設定(スレーブのreco very.co n f)
パラ メ ータ

設定値

内容

stan db y_mo deon

スレーブとして起動か、PITR のリカバリ処理かを区別をする設定。
o n の場合は、スレーブとして起動する。W AL ファイルの最後に達してもリカバリを終了せず、マスタへ接続して新W AL セグメント取得を継続。

p rimary_co n n in fo'h o st= server1 p o rt= 5432
u ser= rep u serp assw o rd= p assw o rd
ap p licatio n _n ame= server2'

マスタへの接続文字列を指定。

h o st はW AL 転送を行う LAN を指定。2 サーバ間の転送のみである事から、
ハートビートLAN でも良い。
p o rt はPo stgreSQ L がリスニングしているポート番号(p o rt パラメータ )。
u ser はrep licatio n 仮想データベースへ接続できるユーザを指定(ユーザのrep licatio n 属性およびp g_h b a.co n f の設定)。
p assw o rd はパスワードが必要な場合に設定。パスワードの記載を避けたい
場合は、~ /.p gp ass を使用。
ap p licatio n _n ame は視認性を高めるのが目的であり必須ではない。ここではスレーブ名を指定。
デフォルトで'w al_receiver' 。
リカバリが作成する個別のタイムラインを指定。
SR 構成ではlatest を指定して最新タイムラインを追従。

reco very_target_timelin e latest
p rimary_slo t_n ame< スロット名>

当該スレーブ用のスロットを作成している場合は、スロット名を設定。
最新バージョンではスロットを作成するのが一般的。

resto re_co mman d

アーカイブW AL ファイルのリストア（コピー）方法を指定

スロットを使用している場合は設定不要

2. 構築手順
ここでは基本的な2 ノード構成における手順を記載します。
表 5.7 SR 構成条件
項目

内容

同期方式

非同期(syn ch ro n o u s_stan db y_n ames = '')レプリケーションスロット

使用しない(1)レプリケーションユーザ作成
マスタサーバでレプリケーション用のユーザを作成します。

postgres=# CREATE ROLE repuser LOGIN REPLICATION PASSWORD 'password';(2)設定ファイルの変更
マスタで$PG DATA 配下のp g_h b a.co n f とp o stgresql.co n f を変更します。

p g_h b a.co n f

Page 20 of 285

PostgreSQL Enterprise Consortium
レプリケーションユーザがデータベース接続できるよう p g_h b a.co n f を次のように設定します。 ここでは便宜上、tru st 認証を指定しています。 md5 認証を指定する場合はパスワードファイル(~ /.p gp ass)を使用して、パスワードの入力が不要となるよ
うに設定します。

$ vi $PGDATA/pg_hba.conf[pg_hba.conf]# デフォルトで以下の設定が登録されています。
local replication
allhost
replication
allhost
replicationall

127.0.0.1/32::1/128

trust
trust
trust

# 以下の様に変更します。
hostreplication
host
replication
host
replication

192.168.100.100/32
192.168.100.101/32
192.168.100.102/32

trust
trust
trust

repuser
repuser
repuserp o stgresql.co n f
マスタとして稼働させるため、p o stgresql.co n f を次のように設定します。 スレーブ側の設定も含めておきます。スレーブ側の設定は、マスタ側では無視されます。

$ vi $PGDATA/postgresql.conf[postgresql.conf]
port = 5432
listen_addresses = '*'wal_level = replica
synchronous_commit = onsynchronous_standby_names = ''max_wal_senders = 10
max_replication_slots = 10
restart_after_crash = off
hot_standby = on
hot_standby_feedback = on

(デフォルト)(デフォルト)(デフォルト)(デフォルト)(デフォルト)(デフォルト)(デフォルト)パラメータの記述後、設定ファイルの変更を反映するために Po stgreSQ L を再起動します。

$ pg_ctl restart(3)p g_b aseb acku p による物理ファイルのコピー (スレーブ）
スレーブ側でp g_b aseb acku p を実行し、スレーブデータベースを構築します。

$ pg_basebackup -h <マスタ IP> -p 5432 -U repuser -D $PGDATA --progress -verbose

なおパスワードファイルはp g_b aseb acku p ではコピーされないため、必要に応じてコピーします。

$ scp <マスタ IP>:~/.pgpass ~/(4)reco very.co n f の設定（スレーブ）
p g_b aseb acku p で取得したデータベースクラスタの設定を変更し、スレーブとして稼働するようにします。 取得先($PG DATA)配下
でreco very.co n f を作成します。 なおスレーブに必要なパラメータ設定は(p o stgresql.co n f)は、マスタ側で設定済みのファイルがコピーされているため、変更不要です。

Page 21 of 285

PostgreSQL Enterprise Consortium

reco very.co n f＄PG DATA 配下に reco very.co n f を作成し、以下を記述します。

$ vi $PGDATA/recovery.conf[recovery.conf]
standby_mode = 'on'
primary_conninfo = 'host=server1 port=5432 user=repuser
password=repuser'
recovery_target_timeline = latest(5)Po stgreSQ L の起動（スレーブ）
スレーブのPo stgreSQ L を起動します。

$ pg_ctl start(6)状況確認（マスタ）SR が構築されていることを確認します。

[マスタ側で確認]
postgres=# \xExpanded display is on.
postgres=# SELECT * FROM pg_stat_replication;-[ RECORD 1 ]----+-----------------------------pid| 40724
usesysid| 16385
usename| repuser
application_name | walreceiverclient_addr| <スレーブのIP>
client_hostname |client_port| 48077
backend_start| 2018-01-04 01:30:54.01041+09
backend_xmin|
state| streaming
sent_lsn| 0/3000060
write_lsn| 0/3000060
flush_lsn| 0/3000060
replay_lsn| 0/3000060
write_lag|
flush_lag|
replay_lag|
sync_priority| 0
sync_state| async(7)状況確認（スレーブ）

Page 22 of 285

PostgreSQL Enterprise Consortium[スレーブ側で確認]
postgres=# \xExpanded display is on.
postgres=# SELECT * FROM pg_stat_wal_receiver;-[ RECORD 1 ]---------+-------------------------------------------------pid| 52484
status| streaming
receive_start_lsn| 0/3000000
receive_start_tli| 1
received_lsn| 0/3000140
received_tli| 1
last_msg_send_time| 2018-01-04 01:35:50.514256+09
last_msg_receipt_time | 2018-01-04 01:18:46.923761+09
latest_end_lsn| 0/3000140
latest_end_time| 2018-01-04 01:33:20.23195+09
slot_name|conninfo| user=repuser password=******** dbname=replication
host=<マスタのIP> port=<マスタのポート番号>
fallback_application_name=walreceiversslmode=disable sslcompression=1
target_session_attrs=any

co n n in fo 列は実際には1 行で表示されますが、便宜上改行しています。

5.2.3. スレー ブの 追加
2 ノード構成からスレーブ(server3)を追加する方法は、基本的には前述の手順と同様です。 複数スレーブ方式とカスケード方式の違いはserver3 側でp g_b aseb acku pを実行するのは同様で、マスタの指定の違いだけです。 カスケード方式では、マスタに負荷をかけないというメリットがあります。

1. p g_b aseb acku p の実行（ server3)（複数スレーブ構成:マスタはsever1）$ pg_basebackup -h server1 -p 5432 -U repuser -D $PGDATA --progress -verbose（カスケード構成 :マスタはserver2）$ pg_basebackup -h server2 -p 5432 -U repuser -D $PGDATA --progress -verbose

2. reco very.co n f の設定（ server3)
reco very.co n f＄PG DATA 配下に reco very.co n f を作成します。 カスケード方式の場合にはserver2 からコピーされたファイルが存在する
ので、それを修正します。 server2 の設定とほぼ同様で、異なるのはマスタの指定です。

$ vi $PGDATA/recovery.conf[recovery.conf]（複数スレーブ構成:マスタはsever1）
primary_conninfo = 'host=server1 port=5432 user=repuser
password=repuser'（カスケード構成 :マスタはserver2）
primary_conninfo = 'host=server1 port=5432 user=repuser
password=repuser'

5.2.4. アー カイブモー ド運用
SR 構成においてもアーカイブモード運用は有力です。 スレーブが物理バックアップとも言えますのでアーカイブモード運用は必須ではありませんが、以下を目的として構成する事もあります。

Page 23 of 285

PostgreSQL Enterprise Consortium
・ PITR (Po in t In Time Reco very)にて一定の過去に戻す
アーカイブモード運用の最大のメリットと言えます。
類似機能として「遅延レプリケーション」もありますが、PITR の方がより汎用的です。
・アーカイブW AL の保持
フルバックアップを1 日1 回取得する場合、アーカイブW AL も1 日分（あるいはそれ以上）保持することになります。
SR の伝搬の遅れが保持時間以内に収まっていれば、SR を継続できます。
W AL を保持する機能としては、レプリケーションスロットがあります。
SR を意識して W AL を確保するため、必要なW AL が欠落するリスクはありません。
ただし伝搬遅延が大きくなりすぎた場合、W AL 領域がディスクフルになるリスクがあります。
アーカイブモード運用の場合は定期的に削除するため、そのリスクは低いと言えます。

1. 関連パラメータ
既存のレプリケーションスロットの設定に加え、次のパラメータを指定します。
サーバ

設定ファイ ルパラ メ ータ

設定値

マスタ

p o stgresql.co n f arch ive_mo de

マスタ

p o stgresql.co n f arch ive_co mman d cp %p < アーカイブの保存先> /%f

スレー
ブreco very.co n f

内容
アーカイブを有効化する。

onresto re_co mman d 'scp < マスタのユーザ名> @ < マスタのホスト
名> :< マスタのアーカイブ・ディレクトリ> %f%p 'アーカイブの実行コマンドを指定する。 スクリ
プトを指定する事も可能であるため、複雑な処理を組み込む事が可能。
マスタのアーカイブを取得するコマンドを指定する。 W AL 同期を保証する場合に必要となる。ただしレプリケーションスロットを使用する
場合は不要である。

注釈

resto re_co mman d にて scp コマンドを使用する場合、スレーブはマスタのPo stgreSQ L のO S ユーザに ssh にてパスワードなしで接続できるようになる必要があります。 またはアーカイブ・ディレクトリをNFS マウント等、マスタ /スレーブ間で共有可能なパスにする事で、 アーカイブの扱いが容易になります。
障害時スレーブをマスタにする場合に備え、マスタに設定したパラメータは スレーブ側でも事前に有効化することを推奨します。

2. 設定手順(1)マスタ側のp o stgresql.co n f のパラメータを以下のように設定する。

$ vi $PGDATA/postgresql.conf[postgresql.conf]
archive_mode = onarchive_command = 'cp %p <アーカイブの保存先>/%f'(2)必要ならばスレーブ側のreco very.co n f のパラメータに以下を追加する。

$ vi $PGDATA/recovery.conf[recovery.conf]
restore_command = 'scp <マスタのユーザ名>@<マスタのホスト名>:<マスタのアーカイブ・ディレクトリ>%f %p'(3)マスタ側を再起動し、その後スレーブ側を再起動する。

[マスタ /スレーブの両方で実施]$ pg_ctl restart(4)マスタ側で強制的に W AL ファイルを切り替え、アーカイブW AL ファイルが出力されることを確認する。

Page 24 of 285

PostgreSQL Enterprise Consortium

postgres=# SELECT pg_switch_xlog();
pg_switch_xlog---------------0/7017008(1 row)$ <アーカイブ・ディレクトリ>
000000010000000000000007

5.2.5. レプリ ケー シ ョ ン スロッ ト
スレーブに未転送のW AL を保持することで、SR の維持を保証します。 アーカイブ運用でも同期の保証は可能ですが、アーカイブログの管理などが問題となり、 ノーアーカイ
ブ運用をしている環境も多くあります。 そのような環境において、レプリケーションの維持を保証するためには、レプリケーションスロットの設定が必要です。 またアーカイブ運用においても、アーカイブW AL ファイルの削除に SR の考慮が不要になるため、有用な設定です。

1. 関連パラメータ
既存のレプリケーションスロットの設定に加え、次のパラメータを指定します。
基本的なSR の構築手順にて設定済みのパラメータも、改めて記載しています。
表 5.8 レプリケーションスロットの設定
サーバ

設定ファイ ルパラ メ ータ

設定値

マスタ

p o stgresql.co n f max_rep licatio n _slo ts

スレーブ数以上（設定済み）

スレー
ブ

p o stgresql.co n f h o t_stan db y_feedb ack o n （設定済み）

スレー
ブ

reco very.co n f

レプリケーションスロット名

p rimary_slo t_n ame

内容
作成可能なレプリケーションスロット数を指定する。
スレーブの状態をマスタにフィードバックする。
使用するレプリケーションスロット名を指定する。

注釈
障害時スレーブをマスタにする場合に備え、マスタに設定したパラメータは スレーブ側でも事前に有効化することを推奨します。

2. レプリケーションスロットの作成方法
レプリケーションスロットに関連する関数は以下の通りです。
表 5.9 レプリケーションスロットの関数
関数名説明

p g_create_p h ysical_rep licatio n _slo t(スロット名[,
tru e/false])

レプリケーションスロットを作成する。
スロット名：作成するレプリケーションスロット名を指定する。
tru e/false ： tru e の場合、レプリケーションスロットは即座に W AL を保持する。
false の場合従来通り、スレーブがレプリケーションスロットに繋いだ時点から
W AL を保持する。

p g_dro p _rep licatio n _slo t(スロット名)

レプリケーションスロットを削除する。
スロット名：削除するレプリケーションスロット名を指定する。

3. 検証(1)マスタにて p o stgresql.co n f に max_rep licatio n _slo ts を加え、設定反映のため再起動。

$ vi $PGDATA/postgresql.conf[postgresql.conf]
max_replication_slots = 10<--- デフォルト$ pg_ctl restart

Page 25 of 285

PostgreSQL Enterprise Consortium(2)マスタにてレプリケーションスロットを作成。

postgres=# SELECT pg_create_physical_replication_slot('slot1', true);
pg_create_physical_replication_slot------------------------------------(slot1,0/B000220)(1 row)(3)マスタにてレプリケーションスロットの作成を確認。

postgres=# SELECT slot_name, restart_lsn, active FROM pg_replication_slots;
slot_name | restart_lsn | active-----------+-------------+-------slot1| 0/B000220 | f(1 row)・ active 列が'f' であることから、まだ使用されていない。
・ restart_lsn 列に値があることから、使用されていなくてもW AL の保持は開始している。

(4)スレーブにて reco very.co n f に p rimary_slo t_n ame を加え、設定反映のため再起動。

$ vi $PGDATA/postgresql.conf[recovery.conf]
primary_slot_name = 'slot1'$ pg_ctl restart(5)マスタ側でレプリケーションスロットが使用されていることを確認。

postgres=# SELECT slot_name, restart_lsn, active FROM pg_replication_slots;
slot_name | restart_lsn | active-----------+-------------+-------slot1| 0/B0002C8 | t(1 row)・ active 列が't' であることから、使用されている。

5.2.6. 【 参考情報】 同期モー ドにお け るパフォ ー マン スへ の 影響
同期モードの懸念として、W AL 転送の待機によるパフォーマンスへの影響があります。 以下の図はsyn ch ro n o u s_co mmit の各設定によるパフォーマンス比較です。(o n の値を1.00 とした相対値）。
サーバススペック

CPU : In tel(R) Xeo n (R) CPU E5-2640 v2 @ 2.00G Hz 16Co re
メモリ : 64G B
O S : Red Hat En terp rise Lin u x Server release 6.5 (San tiago )Po stgreSQ L のバージョン : Po stgreSQ L 9.6.1
パラメータ設定キャッシュヒット率の影響を受けないようほぼ100% となるように調整

sh ared_b u ffers : 8G B
処理中に自動VACU U M が発生しないように無効化
au to vacu u m ： o ff
処理中にチェックポイントが発生しないように調整
ch eckp o in t_timeo u t ： 1h
max_w al_size ： 10G B
処理前に毎回手動でチェックポイント実行
トランザクションツールはp gb en ch を使用。
初期化スケール 100
100 セッション、25 ワーカスレッドのトランザクションを実施(p gb en ch -c 100 -j 25)

Page 26 of 285

PostgreSQL Enterprise Consortium
通常のO LTP アプリケーションではこれほど激しいトランザクションではないため、低下は限定的と考えられます。
同期モードの実装にあたっては、実際のアプリケーションで検証してご確認ください。

5.2.7. 遅延レプリ ケー シ ョ ン
スレーブの適用を一時的に遅延させます。 マスタの操作ミスが即座に伝搬されるのを防ぐのが目的です。 W AL 転送までは遅延なく処理されるため、データ保全には影響ありません。 同期転送(syn ch ro n o u s_co mmit= o n )との組み合わせができます。
注意点・マスタへの切り替わり時に遅延分の適用が発生するため切り替わりに時間がかかります。
高可用性用途には向いていません。
複数スレーブ構成にて、1 台目位は同期、で2 台目を遅延とするする構成が考えられます。
・以下の操作を行うと、遅延レプリケーションの設定(reco very_min _ap p ly_delay)は無視され、最新の状態まで適用されます。ご注意下さい。
- 再起動 (p g_ctl restart)- 昇格 (p g_ctl p ro mo te)・完全同期(syn ch ro n o u s_co mmit= remo te_ap p ly)ではDML(自動コミットがオフの場合はCO MMIT）を待機するため、遅延レプリケーションは使用できません。
以下の検証を行います。
検証1: 遅延レプリケーションの設定および動作確認
スレーブへの適用が指定した時間分、遅延する事。およびマスタでの遅延確認。
検証2: 問題発生前の状態まで適用
オペレーションミス発生を想定し、発生前の状態まで適用し、昇格。

1. 関連パラメータ
既存のレプリケーションスロットの設定に加え、次のパラメータを指定します。
表 5.10 遅延レプリケーションの設定
サーバ

設定ファイ ルパラ メ ータ

設定値

スレーブ

p o stgresql.co n f

h o t_stan db y_feedb ack

スレーブ

reco very.co n f

reco very_min _ap p ly_delay 遅延させる時間を指定

o n （フィードバックを有効化）

2. 検証1 ：遅延レプリケーションの設定および動作確認(1)マスタにてテストに使用するテーブルを作成。

（マスタ）

postgres=# CREATE TABLE delay_test(id integer, time1 timestamp);
postgres=# \d delay_test
Table "public.delay_test"
Column |Type| Modifiers--------+-----------------------------+----------id| integer|time1 | timestamp without time zone |(2)スレーブで遅延レプリケーションの設定を行い、再起動。

Page 27 of 285

PostgreSQL Enterprise Consortium（スレーブ）$ vi $PGDATA/postgresql.conf[postgresql.conf]
hot_standby_feedback = on
$ vi $PGDATA/recovery.conf[recovery.conf]
recovery_min_apply_delay = '60min'$ pg_ctl restart(3)マスタにてテストテーブルにデータを追加。

（マスタ）

postgres=# INSERT INTO delay_test VALUES (1, localtimestamp);
postgres=# SELECT * FROM delay_test;
id |
time1
----+---------------------------1 | 2018-03-10 18:05:46.29221(1 rows)(4)スレーブにて即座に反映されない事を確認。

（スレーブ）

postgres=# SELECT * FROM delay_test;
id | time1----+------(0 rows)<--- INSERTが反映されていない(5)マスタにて適用の遅延を確認。
p g_stat_rep licatio n ビューのrep lay_lag 列で確認

（マスタ）

postgres=# SELECT write_lag,flush_lag,replay_lag FROM pg_stat_replication ;-[ RECORD 1 ]--------------write_lag | 00:00:00.000323
flush_lag | 00:00:00.00048
replay_lag | 00:00:38.354572 <--- recovery_min_apply_delayに達するまで適用を待機(6)１時間経過後、スレーブにて適用された事を確認。

（スレーブ）

postgres=# SELECT *,localtimestamp FROM delay_test;
id |
time1
|
localtimestamp----+----------------------------+---------------------------1 | 2018-03-10 18:05:46.29221 | 2018-03-10 19:06:10.521195(q rows)(7)マスタにて適用の遅延がクリアされた事を確認。

Page 28 of 285

PostgreSQL Enterprise Consortium（マスタ）

postgres=# SELECT write_lag,flush_lag,replay_lag FROM pg_stat_replication ;-[ RECORD 1 ]--------------write_lag |flush_lag |replay_lag |3. 検証2 ：問題発生前の状態まで適用(1)マスタにてテストテーブルにデータを追加。

（マスタ）

postgres=# INSERT INTO delay_test VALUES (2, localtimestamp);
postgres=# SELECT * FROM delay_test;
id |
time1
----+---------------------------1 | 2018-03-10 18:05:46.29221
2 | 2018-03-10 19:30:11.23870(1 rows)

id= 2 の更新(INSERT)をオペレーションミスとみなし、スレーブを更新前の時点まで適用(2)マスタを停止。

$ pg_ctl stop(3)スレーブにて 1 件も反映されない事を確認。

（スレーブ）

postgres=# SELECT * FROM delay_test;
id | time1----+------(0 rows)<--- INSERTが反映されていない(4)スレーブを停止。

$ pg_ctl stop(5)スレーブにて reco very.co n f を修正。

$ vi $PGDATA/recovery.conf[recovery.conf]
restore_command = 'cp <$PGDATAパス>/pg_wal/%f %p' <--- WALのパスを指定
recovery_target_time = '2018-03-10 19:30:00'<--- id=2のCOMMIT直前の時刻
recovery_target_timeline = latest<--- 最新のタイムライン
recovery_target_action = 'promote'<--- 適用中断後、昇格(6)スレーブにて指定時間までのリカバリおよび昇格(スレーブ→ 新マスタ）

Page 29 of 285

PostgreSQL Enterprise Consortium$ pg_ctl start$ pg_controldata | grep state
Database cluster state:in production<--- マスタ /スレーブの確認<--- マスタ

表 5.11 遅延レプリケーションのPITR ログ
メ ッセージ

時間

LO G : startin g p o in t-in -time reco very to 2018-03-10 19:30:00+ 09

reco very_target_time

LO G : reco very sto p p in g b efo re co mmit o f tran sactio n 578, time 2018-03-10 19:30:11.423701 id= 2 のCO MMIT時間
LO G : last co mp leted tran sactio n w as at lo g time 2018-03-10 18:05:46.314276

id= 1 のCO MMIT時間(8)新マスタにて id= 1 のみ適用されていることを確認。

postgres=# SELECT * FROM delay_test ;
id |
time1
----+--------------------------1 | 2018-03-10 18:05:46.29221(1 row)

5.2.8. WAL 圧縮
Fu ll Page W rite 時(チェックポイント後の最初の更新時)に、W AL に書き出すフルページイメージを圧縮します。 圧縮された W AL は適用時に解凍されます。 W AL ファイルのサイズが小さくなるため、書き込み や転送の時間短縮 が期待されます。 注意点として、圧縮処理および解凍処理が発生するため、通常より余分に CPU を使用します。 適用についてはそれらを総合的に判断します。 一般的な適用場面として、SR 構成において効果的と考えられます。 特に以下の構成で有力です。
同期モード（転送まで）または完全同期モード（適用まで）
W AL 転送の遅延を懸念して同期モード設定を躊躇する場面でW AL 圧縮を検討します。
非同期モードでもW AL 圧縮は有力ですが、一定のW AL 転送の遅延は許容されるため、マスタのCPU 負荷を優先して W AL 圧縮を適用しないという判断も考えられます。
スレーブの遠隔地配置（ディザスタ・リカバリ）
スレーブを遠隔地に配置する構成において、非同期モードでもW AL 転送の遅延が許容範囲を超える場合に W AL 圧縮の適用を検討します。

1. 関連パラメータ
既存のレプリケーションスロットの設定に加え、次のパラメータを指定します。
表 5.12 W AL 圧縮の設定
サーバ
マスタ

設定ファイ ルパラ メ ータp o stgresql.co n f w al_co mp ressio n

設定値'o n '

5.3. SR環境の監視
5.3.1. レプリ ケー シ ョ ン 操作ログの 監視
5.3.1.1. 調査の目的
SR 構成のサーバログ監視について有益な情報を提示する事を目的としています。
以下の挙動時にサーバログに出力されるメッセージを確認しました。
マスタの起動/停止
スレーブの起動/停止
カスケードスレーブの起動/停止
マスタ側のW AL 再利用によるロスト(スロット不使用）
カスケードスレーブ構成時のスレーブ側のW AL 再利用によるロスト(スロット不使用）
以下のパターンで検証しました。
レプリケーション操作ログ出力無効時（デフォルト）

Page 30 of 285

PostgreSQL Enterprise Consortium

lo g_rep licatio n _co mman d = o ff
レプリケーション操作ログ出力有効時

lo g_rep licatio n _co mman d = o n
5.3.1.2. 調査結果
マスタ側またはスレーブ側のサーバログに出力されるレプリケーション情報についてまとめます。
ホスト名、IP アドレス、ポート番号、ユーザ名、データベース名は例です。
レプリケーション操作ログ出力無効時（デフォルト）
表 5.13 デフォルトで出力される情報
タ イ ミ ング

サイ ト

スレーブ停止時

マスタ

メ ッセージ
スレーブが複数台存在し、Q u o ru m-b ased 同期レプリケーションを設定していない場合に、片系スレーブの起動または停止を行うことで同期優先順位が変化した場合のみされます。

START_REPLICATIO N 0/57000000 TIMELINE 1
マスタ停止時

スレーブ

エラーメッセージが繰り返し出力されます。

LO G : in valid reco rd len gth at 132/EF8AD7F0: w an ted 24, go t 0
FATAL: could not connect to the primary server: co u ld n o t co n n ect to server: Co n n ectio n refu sed
Is th e server ru n n in g o n h o st "master" (192.168.100.100) an d accep tin g
TCP/IP co n n ectio n s o n p o rt 5432?
W AL 再利用による マスタ

エラーメッセージが繰り返し出力されます。

ロスト

ERRO R: requ ested W AL segmen t 0000001100000132000000EF h as already been removed

W AL 再利用による スレーブ

エラーメッセージが繰り返し出力されます。

ロスト

LO G : started streamin g W AL fro m p rimary at 132/EF000000 o n timelin e 17
FATAL: could not receive data from WAL stream: ERRO R: requ ested W AL segmen t
0000001100000132000000EF h as already been removed
レプリケーション操作ログ出力有効時

デフォルトで出力される情報に加えて、以下が出力されます。
表 5.14 監視強化時に出力される情報
タ イ ミ ング
スレーブ起動ま
たは停止時

サイ ト
マスタ

メ ッセージ
スレーブが複数台存在し、Q u o ru m-b ased 同期レプリケーションを設定していない場合に、片系スレーブの起動または停止を行うことで同期優先順位が変化した場合のみ出力されます。

LO G : received rep licatio n co mman d: IDENTIFY_SYSTEMLO G : received rep licatio n co mman d:
START_REPLICATIO N 0/57000000 TIMELINE 1
W AL 再利用によるロスト

マスタ、スレーブ(スレーブ出力はカス
ケード・レプリケー
ション時のみ)エラーメッセージが繰り返し出力されます。

LO G : received rep licatio n co mman d: IDENTIFY_SYSTEMLO G : received rep licatio n co mman d: START_REPLICATIO N 134/4A000000 TIMELINE 17

5.3.1.3. 調査結果
SR 関連のエラーが発生した場合は、デフォルトの設定(lo g_min _messages= 'w arn in g')でサーバログに出力されます。
表の太字の文字が監視キーワード候補です。
マスタが停止するとスレーブに出力がありますが、スレーブが単一の構成時にはスレーブが停止してもマスタに出力はありませんでした。
lo g_rep licatio n _co mman d による出力はそれほど多くなく、監視に対する影響は限定的です。
ただリロードで反映できることと合わせて、気軽に有効化できるとも言えます。
lo g_min _message = DEB U G [1-5] (例)DEB U G 1 、DEB U G 2 とすることでもレプリケーション関連のメッセージが出力されます。
ただし影響が大きいため、通常はlo g_rep licatio n _co mman d を使用します。
lo g_rep licatio n _co mman d による出力は、スレーブのw al receiver プロセスからフィードバックされた情報です。
lo g_lin e_p refix に ap p licatio n _n ame(%a)を設定する事で確認できます。
SQ LSTATE の値をキーワードに監視を行うために、lo g_lin e_p refix パラメータに SQ LSTATE (%e)を設定します。

5.3.2. 同期状況の 監視
非同期レプリケーションを構成した場合、ハードやNW 構成、利用状況によってマスタとスレーブの同期状況が変化します。
この状態を監視し、同期の遅延が許容範囲内であるかを確認します。

■ 前提Page 31 of 285

PostgreSQL Enterprise Consortium
構成：
マスタ下に 1 台または2 台のスレーブ構成
マスタ、スレーブに加えカスケードスレーブをスレーブ下に構成する計3 台構成（カスケード・レプリケーション）
バージョン： Po stgreSQ L 10
同期モード：問わない

5.3.2.1. 同期遅延監視(1)W AL の書き込み位置による同期遅延量を用いて監視することは可能か「 2014 年度W G 3 活動報告書- 可用性編 -」での検証結果をベースに実施します。
マスタのW AL とスタンバイが適応した W AL の2 つの差分は、マスタの最新W AL の書き込み位置(LSN ： Lo g Sequ en ce Nu mb er)とスタンバイが適用した W AL の位置(LSN)との差分より算出します。
マスタのLSN は、p g_cu rren t_w al_in sert_lsn ()関数が返す現在のW AL の挿入位置とみなします。
スタンバイのLSN は、統計情報p g_stat_rep licatio n ビューのrep lay_lsn を使用します。
この2 つのLSN の差分をバイト単位で表示するために、p g_w al_lsn _diff 関数の引数にそれぞれを格納しその結果が許容値を超過するかどうかで、監視を行います。
検証には、以下シェルスクリプトで実施しました。
監視通知部については、テストの為、標準出力のみとしています。

Page 32 of 285

PostgreSQL Enterprise Consortium

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

#!/bin/sh
HOST=localhost
LIMIT=1
SLEEPTIME=2
ALERT="echo"
SLAVENUM=2
function send_alert(){${ALERT} $1}
IFS_DEFAULT=$IFS
while :
doval1=$(psql -h $HOST -p 5432 -U postgres -q -t -c "select current_time;")case $(psql -p 5432 -U postgres -q -t -c "SELECT pg_is_in_recovery();"|awk '{print $NF}') int)
val2=$(psql --no-align -h $HOST -p 5432 -U postgres -q -t -c"select client_addr,pg_wal_lsn_diff(master,replay_lsn)
as replaydiff from (select pg_last_wal_replay_lsn() master)as m,pg_stat_replication;");;f)val2=$(psql --no-align -h $HOST -p 5432 -U postgres -q -t -c"select client_addr,pg_wal_lsn_diff(master,replay_lsn)
as replaydiff from (select pg_current_wal_insert_lsn() master)as m,pg_stat_replication;");;esac
rownum=$(psql --no-align -h $HOST -p 5432 -U postgres -q -t -c"select client_addr,pg_wal_lsn_diff(master,replay_lsn)
as replaydiff from (select pg_current_wal_insert_lsn() master)as m,pg_stat_replication;" |wc -l)for slave_lsn in ${val2};doif [ ${rownum} -ne ${SLAVENUM} ]; thensend_alert "Slave Down |${val1}"
fi
IFS=$'|'
byte=`echo ${slave_lsn} | awk '{print $NF}'`
if [ ${byte} -gt ${LIMIT} ]; thensend_alert "Replication Delay ${val1},${slave_lsn}"fi
IFS=${IFS_DEFAULT}
sleep ${SLEEPTIME}
done
done

今回の検証でスレーブが複数存在する場合も、それぞれのスレーブの遅延状況を検知することが可能であることがわかりました。
監視シェルを実行中に in sert 文を実行した結果を記載します。
以下の通り、遅延状況を検知でき、メールサーバ等に連携することで監視することが可能です。
また間接的に更新待ち監視も行うことができます。詳細は、更新・読取監視の項に記載します。

Page 33 of 285

PostgreSQL Enterprise Consortium$ ./delay_test.sh
Replication Delay
Replication Delay
Replication Delay
Replication Delay
Replication Delay
Replication Delay
Replication Delay

20:58:18.451839+09,192.168.100.101 11880
20:58:18.451839+09,192.168.100.102 11880
20:58:22.484532+09,192.168.100.101 11880
20:58:22.484532+09,192.168.100.102 11880
20:58:26.512656+09,192.168.100.101 12048
20:58:26.512656+09,192.168.100.102 12048
20:58:30.538009+09,192.168.100.102 12048

尚、カスケードスレーブ構成の場合にはスレーブ上で同シェルスクリプトを実行することで、カスケードスレーブとの同期の遅延状況を検知することが可能です。

5.3.3. 更新、 読み 取りの 監視
非同期レプリケーションを構成した場合、ハードやNW 構成、利用状況によってマスタとスレーブの同期状況が変化します。
この状態を監視し、同期の遅延が許容範囲内であるかを確認します。

■ 前提構成3 ノード構成
1. 複数スレーブ方式（親 - 子1 , 子2 ） スレーブ2 はマスタと繋がっている。

2. カスケード方式（親 - 子 - 孫） スレーブ2 はスレーブ1 と繋がっている

同期モード：同期または完全同期 (syn ch ro n o u s_co mmit = o n / remo te_ap p ly)

5.3.3.1. 更新・ 読取監視
(1)Po stgreSQ L の機能により監視することは可能か- 検証方法Po stgreSQ L の内部パラメータ (statemen t_timeo u t)により、マスタへの更新処理に対して
スレーブ停止によって一定時間応答がない場合に接続を切断し、アラート通知することは可能かを検証を行いました。
statemen t_timeo u t は、p o stgresql.co n f 内での設定は推奨されていない為、SQ L 発行時に設定しました。
また、p g_stat_rep licatio n ビューにより間接的に、検知できないかについて検証を行いました。

- 検証結果statemen t_timeo u t を設定しSQ L を発行しましたが、有効に機能せず
Ctrl+ C をキーインするまで停止しないという結果となりました。
複数台のスレーブのうち１つでも稼働している場合には設定時間を待たずにPage 34 of 285

PostgreSQL Enterprise Consortium
一方のスレーブの応答をもって処理が完了してしまうため、他のスレーブの同期遅延の検知ができないという結果となりました。
全て のスレーブが応答不可能である場合$ psql -U postgres -d testdb -q -t << EOF> \timing on> SET statement_timeout TO 5000;> insert into test(id) values(1);> delete from test where id=1;> EOF
時間: 0.192 ms^CCancel request sent
WARNING: canceling wait for synchronous replication due to user request
DETAIL: The transaction has already committed locally, but might not have been
replicated to the standby.
時間: 13626.496 ms

一方のスレーブのみ 応答が可能である場合$ psql -U postgres -d testdb -q -t << EOF> \timing on> SET statement_timeout TO 5000;> insert into test(id) values(1);> delete from test where id=1;> EOF
Time: 0.147 ms
Time: 1.899 ms
Time: 1.277 ms

p g_stat_rep licatio n ビューについては、スレーブサーバ停止時にレコードが取得できなくなる為
この点を利用することで間接的に、同期待ちを検知することが可能であることがわかります。

testdb=# select * from pg_stat_replication;pid | usesysid | usename | application_name | client_addr | client_hostname | client_port |backend_start| backend_xmin | state | sen
t_lsn | write_lsn | flush_lsn | replay_lsn | write_lag | flush_lag | flush_lag |sync_priority | sync_state-------+----------+---------+------------------+----------------+----------------+-------------+-------------------------------+--------------+-----------+--------------+----------------+----------------+-----------------+---------------+-----------(0 行)(2)O S コマンドなどの外部機能により、更新処理に対して一定時間応答がない場合を監視する
スレーブが単一であればtimeo u t コマンドにより、指定秒数で強制切断に成功し、スタンバイと同期が取れていないメッセージが出力されます。
また終了値として、124 が返却されている為、全てのスレーブが応答できない場合に限り、メッセージ内容または、終了値を制御することでスレーブサーバ障害による同期待ちを検知することが可能です。
スレーブが単一である場合

Page 35 of 285

PostgreSQL Enterprise Consortium$ timeout -sINT 5 psql -U postgres -d testdb -q -t << EOF> \timing on> insert into test(id) values(1);> delete from test where id=1;> EOF
Cancel request sent
WARNING: canceling wait for synchronous replication due to user request
DETAIL: The transaction has already committed locally, but might not have been
replicated to the standby.
Time: 4994.166 ms (00:04.994)[postgres@test_pg01 data]$ echo $?
124

しかし statemen t_timeo u t 設定による検証のケースと同様に、複数台のスレーブのうち１つでも稼働している場合には設定時間を待たずに一方のスレーブの応答をもって処理が完了してしまうため、他のスレーブの同期遅延の検知ができないという結果となりました。
一方のスレーブのみ 応答が可能である場合$ timeout -sINT 5 psql -U postgres -d testdb -q -t << EOF> \timing on> insert into test(id) values(1);> delete from test where id=1;> EOF
Time: 2.382 ms
Time: 1.246 ms[postgres@test_pg01 data]$ echo $?
0

5.3.4. スプリ ッ トブレイン の 監視
5.3.4.1. スプリット ブレイ ンの定義と 調査の目的
SR 構成においては、通常はマスタの障害を検知した場合にスレーブを昇格させます。つまりマスタは常に 1 台のみです。 ただしオペレーションミスにより、マスタが正常な状態にもかかわらずスレーブを昇格させてしまうという事も有り得ます。 稼働中のマスタ（シングル）が2 台という危険な状態になります。その状態をスプリットブレインと定義します。
その場合でも全てのアプリケーションが元のマスタにのみ接続していれば問題ありませんが、 2 台目のマスタにも接続が発生するとデータの整合性が損なわれてしまいます。
それを避けるため、スプリットブレイン状態になっていないかの監視の方法を検討します。

5.3.4.2. 監視方法
以下の監視についてまとめます。
サーバログの監視

p g_co n tro ldata コマンドによる監視
p g_co n tro l_reco very関数による監視
p g_is_in _reco very関数による監視
5.3.4.3. サーバロ グの監視
p g_ctl p ro mo te コマンドにより昇格した場合は、昇格したノードのサーバログに以下のメッセージが出力されます。 timeline ID が変化 した事が分かります。

received promote request
selected new timeline ID: XX

マスタ稼働中にスレーブ側にこのようなメッセージが出力されていないかを監視します。
注意点

reco very.co n f を削除（または改名）した後に再起動する事でマスタ（シングル）として起動する方法もあります。
ただし timelin e ID の変化がなく、特徴的なメッセージは出力されません。状態変更の捕捉が困難です。
5.3.4.4. pg_controldata コ マンド に よる監視
Page 36 of 285

PostgreSQL Enterprise Consortium

5.3.4.4. pg_controldata コ マンド に よる監視p g_co n tro ldata コマンドは制御ファイル($PG DATA/glo b al/p g_co n tro l)の状態を表示します。
以下に実行例を示します。ここでは環境変数LANG の設定により英語で表示しています。
(実行例)$ export LANG=C$ pg_controldata
pg_control version number:
Catalog version number:
Database system identifier:Database cluster state:1002
201707211
6514575400714084610
in archive recovery～以下略～

多数の項目がありますが、ここでは"Datab ase clu ster state"に着目します。 必要な項目のみ抽出する例です。

(スレーブでの実行例)$ pg_controldata | grep "Database cluster state"
Database cluster state:in archive recovery

4 種類の状態があります。
マスタ /スレーブ
稼働中/停止中表 5.15 Datab ase clu ster state で表示される値
表示さ れ る値 ( 英語）

表示さ れ る値 ( 日本語）意味

in p ro du ctio n

運用中

マスタとして稼働中

in arch ive reco very

アーカイブリカバリ中

スレーブとして稼働中

sh u t do w n

シャットダウン

マスタとして停止中

sh u t do w n in reco very

リカバリしながらシャットダウン中

スレーブとして停止中"in arch ive reco very"はPITR としてのリカバリ中の場合も含みますが、ここではスレーブ状態の意味です。
カスケード・レプリケーション構成の場合は、スレーブもリカバリ中との取り扱いになるためカスケードスレーブと同様にスレーブ状態に準じた状態が表示されます。
両ノードとも"in p ro du ctio n "状態でない事を確認します。

ssh コマンドでリモートの状態を容易に取得できます。
注意点としては、リモート側の環境変数を認識しないため、明示的に指定する必要があります。
以下はローカルとリモートで同一設定の前提です。
(スプリットブレインの状態下での実行例)$ ssh <remote> $PGHOME/bin/pg_controldata $PGDATA | \> grep "Database cluster state"
Database cluster state:in production

5.3.4.5. pg_control_recovery コ マンド に よる監視p g_co n tro l_reco veryは制御ファイルの情報をp g_co n tro ldata コマンドに代わり取得する方法です。
取得できる項目は限られていますが、SELECT文で取得できるという特徴があります。
状態監視の選択肢の一つとしてご認識下さい。
(スレーブでの実行例)

Page 37 of 285

PostgreSQL Enterprise Consortium=# SELECT pg_control_recovery();pg_control_recovery----------------------------(0/5E0001B0,1,0/0,0/0,f)(1 行)

カンマ区切りにより5 項目から構成されています。何れもp g_co n tro ldata コマンドでも取得できます。
表 5.16 p g_co n tro l_reco very関数の表示内容例
項目

マスタ

スレーブ

min _reco very_en d_lsn

0/0

0/15000178

min _reco very_en d_timelin e

01b acku p _start_lsn

0/0

0/0

b acku p _en d_lsn

0/0

0/0

en d_o f_b acku p _reco rd_requ iredffmin _reco very_en d_lo catio n および min _reco very_en d_timelin e にマスタとスレーブの違いが表れます。
ただしp g_co n tro ldata コマンドによる監視と同様、カスケード・レプリケーション構成の場合はスレーブもリカバリ中との取り扱いになるため、例外的にカスケードスレーブとの違いが表れません。

5.3.4.6. pg_is_in_recovery 関数に よる監視
リカバリ中かどうかを示します。 マスタであれば f (false) 、スレーブであれば t (tru e) を表示します。

(スレーブでの実行例)=# SELECT pg_is_in_recovery();pg_is_in_recovery------------------t(1 行)

両ノードとも f (false) 状態でない事を確認します。

5.4. SR環境の障害時運用
5.4.1. 本文書にお け る用語の 定義
5.4.1.1. フェ イ ルオーバ
マスタへの障害発生時やアクセス不能時に、スレーブをマスタへ昇格させる処理を指します。
新マスタはシングル構成であり、レプリケーションされていない片系運用の状態です。
レプリケーション構成への復旧には後述の「フェイルバック」を実行しますが、マスタの障害がインスタンス障害など、データファイルの障害でない場合は、「スイッチオーバ」が可能な場合もあります。

5.4.1.2. フェ イ ルバック
フェイルオーバにより片系運用になった後、再度レプリケーション構成に復帰させる処理を指します。
シングル構成となった新マスタに、新スレーブを追加します。
新マスタと新スレーブを交代させてフェイルオーバ前に戻すことまでは含まない事とします。
複数スレーブ構成では、不要な処理となります。

Page 38 of 285

PostgreSQL Enterprise Consortium

5.4.1.3. スイ ッチ オーバ
マスタとスレーブを入れ替える処理を指します。
マスタを停止し、スレーブを新マスタへ昇格させ旧マスタを新スレーブとして再追加します。
マスタのメンテナンスのための一時的な入れ替えなどが目的です。
フェイルオーバーとの違いは、マスタ停止が障害による停止か計画停止かであり、スレーブに対する処理は同様です。

5.4.1.4. スイ ッチ バック
スイッチオーバ後に、マスタ /スレーブを入れ替えることを指します。
マスタ /スレーブとの関係性を除き処理内容は、「スイッチオーバ」と同様です。

Page 39 of 285

PostgreSQL Enterprise Consortium

5.4.2. 障害時運用手順（ 2ノ ー ド構成）
■ 前提 以降の手順では次の前提とします。
Po stgreSQ L 10.1
マスタ、スレーブの2 台構成（ホスト名をそれぞれ server1,server2 と表記する）
同期モードは同期（ syn ch ro n o u s_co mmit = o n または remo te_ap p ly)
スレーブはホットスタンバイ機能により参照可能 (h o t_stan db y = o n )レプリケーション用のユーザは rep _u ser
マスタ /スレーブとも、p o rt は5432 を使用
レプリケーションスロット使用
マスタ /スレーブとも、環境変数PG DATA,PG PO RTは設定済み
死活監視は実際の運用ではクラスタソフトを使用するのが一般的ですが、ここでは便宜上手動で実施
サーバのNIC はパブリックのみ
仮想IP については考慮しない■ 対処一覧 大別すると3 種類の対処方法が考えられます。

Page 40 of 285

PostgreSQL Enterprise Consortium
表 5.17 障害別の対処

ID

障害箇
所

1

マスタ

障害状況

pg_basebackup と pg_rewind の使い分け

マスタとスレーブの関係が崩れており再構成が必要

p g_b aseb acku p コマンドを使用してフェイルバック

物理障害

p ro mo te を伴わないスレーブのマスタ化(reco very.co n f 削除）
2

マスタ

マスタとスレーブの関係は巻き戻しで復旧可能

p g_rew in d コマンドを使用してスイッチバック

非同期レプリケーションのインスタンス障害（差異がある状態で昇格）
スプリットブレイン状態での旧マスタへの更新

3

マスタ

マスタとスレーブの切り替え可能

p g_rew in d コマンドを使用しないでスイッチバック

計画停止
同期レプリケーションのインスタンス障害(物理障害なし）

4

スレーブ

マスタとスレーブの連携再開可能

同期式の場合は非同期式に切り替え

スレーブの障害

5.4.2.1. フェ イ ルオーバ
フェイルオーバについて記載します。
マスタにて、障害が発生した場合のスレーブを新マスタへ昇格したシングル構成図(1) マスタの疑似障害発生
immediate オプションにて停止、またはp o stgres プロセスのkill する事で擬似障害を発生させます。
マスタにて実行します。

$ pg_ctl -w -m immediate stop

$ kill -9 `head -1 $PGDATA/postmaster.pid`(2) マスタの死活監視にて異常を検知
死活監視の方法には多数ありますが、ここではp g_isreadyコマンドを使用します。
p g_isreadyコマンドはクライアントツールですので、任意のノードから実行できます。

$ pg_isready -h server2 -U postgres -d postgres
server2:5432 - no response(3) スレーブを新マスタへ昇格
旧スレーブで実行します。

$ pg_ctl promote※サーバログに下記内容が記載されること"datab ase system is ready to accep t co n n ectio n s"
ただしこの時点ではsyn ch ro n o u s_stan db y_n ames パラメータに値が設定されているため、新マスタで更新処理ができない状態です。

Page 41 of 285

PostgreSQL Enterprise Consortium(4) 新マスタを非同期に切り替え
syn ch ro n o u s_stan db y_n ames パラメータの設定を'' に設定し、リロードで反映します。
新マスタで実行します。

$ vi $PGDATA/postgresql.conf[編集前]
synchronous_standby_names = '*'[編集後]
synchronous_standby_names = ''$ pg_ctl reload
これで更新処理ができる状態になりました。

(5) 新マスタの死活監視にて正常を確認$ pg_isready -h server2 -U postgres -d postgres
server2:5432 - accepting connections
以上でファイルオーバーは完了です。

5.4.2.2. フェ イ ルバック
p g_b aseb acku p を使用したフェイルバックについて記載します。 初期構築手順とほぼ同じです。
マスタ障害発生によるフェイルオーバ後、旧マスタを新スレーブとしたレプリケーション構成図■ パラメータp g_b aseb acku p に必要な設定を記載します。
表 5.18 p g_b aseb acku p に必要な設定
サーバ

設定ファイ ルパラ メ ータ

設定値

内容

マスタ

p o stgresql.co n f listen _address

0.0.0.0

全てのIP アドレス(v4)からの接続を受け付ける

マスタ

p o stgresql.co n f max_w al_sen ders2

W AL ストリームオプションを付与する場合は、2 以上を設定■p g_b aseb acku p コマンドp g_b aseb acku p コマンドの主なオプションは次の通りです。

Page 42 of 285

PostgreSQL Enterprise Consortium
表 5.19 p g_b aseb acku p のオプション
オプショ ン

内容-D < directo ry>

出力を書き出すディレクトリを指定。

-X < meth o d>--w al-meth o d= < meth o d>

必要なW AL ファイルをバックアップに含める。meth o d(収集方式)は以下から選択。

fetch ： W AL ファイルは最後に収集
stream ：バックアップ作成中に同時に W AL をストリームで収集運用中に p g_b aseb acku p を実行する場合には stream を指定する。
fetch （最後に収集）では、必要なW AL ファイルが削除される可能性があるため。

-S < slo t_n ame>--slo t= < slo t_n ame>

W AL ストリーミングの収集に指定したレプリケーションスロットを使用。
-X stream とセットで指定。
必要なW AL ファイルが削除されるのを防ぐ事を目的とする。
運用中に p g_b aseb acku p を実行する場合に使用を検討する。
以下に注意する。
事前にレプリケーションスロットを作成する必要がある
マスタのW AL 領域の空きが十分である事を確認する-R--w rite-reco very-co n f

最低限のreco very.co n f を作成。必要に応じて reco very.co n f を加筆修正。

-r < rate>--max-rate= < rate>

サーバから転送されるデータの最大転送速度を指定。
運用中に p g_b aseb acku p を実行する場合に使用を検討する。
転送速度を抑える事で、マスタに対する影響を制限する事を目的とする。

-P

進行状況報告を有効化。
p g_b aseb acku p 処理中におおよその進行状況を報告する。
運用中に実行した場合、データベースクラスタのサイズが増加して進行状況が100% を超える場合がある。

-v

冗長モードを有効化。
進行状況報告も有効な場合、現在処理中のファイル名を出力。

p g_b aseb acku p でレプリケーションスロットが使用できます。 W AL 収集方式に stream を指定する事でW AL をほぼ確保できますが、スロットを指定する事でより確実になります。 スロットを使用する運用であれば、この段階で作成するのが有力です。

■ フェイルバック手順
p g_b aseb acku p にスロットを指定する場合を記載します。
(1) 旧マスタのデータベースクラスタを削除
旧マスタにて実行します。

$ rm -rf $PGDATA/*※$PG DATA 以外に表領域を作成している場合、そのファイルも削除します。
※$PG DATA ディレクトリを削除する場合は、p o stgres ユーザで$PG DATA ディレクトリを作成できるよう
親ディレクトリのオーナーまたはパーミッションを設定します。

(2) レプリケーションスロットの作成
新マスタにて実行します。

Page 43 of 285

PostgreSQL Enterprise Consortium$ psql=# SELECT pg_create_physical_replication_slot('slot_server1',true); -- 第2パラメータに trueを指定
pg_create_physical_replication_slot------------------------------------(slot_server1,144/EEFC8940)(1 行)=# \x on
拡張表示は on です。

=# SELECT * FROM pg_replication_slots ;-[ RECORD 1 ]-------+------------slot_name| slot_server1
plugin|slot_type| physicaldatoid|database|
active| f-- まだスロットは使用されてないため false
active_pid|
xmin|
catalog_xmin|
restart_lsn| 144/EEFC8940 -- trueの指定により、作成直後から restat_lsnを認識
confirmed_flush_lsn |(3) p g_b aseb acku p コマンドにて、新マスタからデータベースクラスタをコピー
旧マスタにて実行します。

$ pg_basebackup -h server2 -U rep_user -D $PGDATA -X stream -S slot_server1 -P -v -Rtransaction log start point: 144/F3000028 on timeline 17
pg_basebackup: starting background WAL receiver
10503022/10503022 kB (100%), 1/1 tablespacetransaction log end point: 144/F3000130
pg_basebackup: waiting for background process to finish streaming ...
pg_basebackup: base backup completed(4) reco very.co n f の修正
旧マスタにて実行します。
p g_b aseb acku p にて -R オプションを指定した事で、reco very.co n f が作成されます。
スロットを指定している事から、p rimary_slo t_n ame の指定があります。以下を追記します。

reco very_target_timelin e パラメータp rimary_co n n in f パラメータに ap p licatio n _n ame を追加（任意／デフォルトはw alreceiver ）$ vi $PGDATA/recovery.conf[編集前]
standby_mode = 'on'
primary_conninfo = 'user=rep_user host=server2 port=5432 sslmode=prefer sslcompression=1'
primary_slot_name = 'slot_server1'[編集後]
standby_mode = 'on'
primary_conninfo = 'user=rep_user host=server2 port=5432 application_name=slave_server1
asslmode=prefer sslcompression=1'
primary_slot_name = 'slot_server1'
recovery_target_timeline = latest(5) 旧マスタのp o stgresql.co n f の修正
旧マスタにて、パラメータの調整を行います。

syn ch ro n o u s_stan db y_n ames パラメータの無効化
p g_statsin fo の無効化(有効化されている場合）
同期レプリケーションの設定を無効化します。

Page 44 of 285

PostgreSQL Enterprise Consortium
新スレーブのsh ared_p relo ad_lib raries パラメータに p g_statsin fo が設定されている場合は、書き込みができずにエラーが発生します。
p g_statsin fo の指定を削除します。

$ vi $PGDATA/postgresql.conf[編集前]
synchronous_standby_names = '*'shared_preload_libraries = 'pg_stat_statements,pg_statsinfo'[編集後]
synchronous_standby_names = ''shared_preload_libraries = ''(6) 新スレーブの起動
新スレーブを起動します。

$ pg_ctl start(7) 新マスタでのレプリケーション確認
p g_stat_rep licatio n s ビューを参照して、レプリケーション構成である事を確認します。
p g_rep licatio n _slo ts ビューを参照して、スロットがアクティブである事を確認します。

$ psql=# SELECT * FROM pg_stat_replication;-[ RECORD 1 ]----+-----------------------------pid| 11262
usesysid| 16384
usename| rep_user
application_name | slave_server1
client_addr| <新スレーブIP>
client_hostname |client_port| 44548
backend_start| 2017-03-22 06:36:06.362576+09
backend_xmin| 1781state| streaming--- ストリーミング中
sent_location| 0/10000060
write_location | 0/10000060
flush_location | 0/10000060
replay_location | 0/10000000
sync_priority| 2
sync_state| async--- 非同期=# SELECT slot_name,active FROM pg_replication_slots;-[ RECORD 1 ]-------+------------slot_name| slot_server1
active| t--- アクティブ(8) レプリケーション方式を同期式に変更
新マスタにて、syn ch ro n o u s_stan db y_n ames パラメータを設定し、リロードで反映します。

$ vi $PGDATA/postgresql.conf[編集前]
synchronous_standby_names = ''[編集後]
synchronous_standby_names = '*'$ pg_ctl reload

Page 45 of 285

PostgreSQL Enterprise Consortium$ psql -h server2 -U postgres postgres -c "SELECT * FROM pg_stat_replication" -x-[ RECORD 1 ]----+-----------------------------pid| 11262
usesysid| 16384
usename| rep_user
application_name | s1
client_addr| <新スレーブIP>
client_hostname |client_port| 44548
backend_start| 2017-03-22 06:36:06.362576+09
backend_xmin| 1781state| streaming--- ストリーミング中
sent_location| 0/10000060
write_location | 0/10000060
flush_location | 0/10000060
replay_location | 0/10000000
sync_priority| 2
sync_state| sync--- 同期
これにて、以下の構成に復旧しました。
レプリケーションスロット使用
同期式レプリケーション構成

5.4.2.3. スイ ッチ オーバ
スイッチオーバについて記載します。

■ スイッチオーバ手順
計画停止におけるマスタ /スレーブの切り替え手順です。 p g_b aseb acku p やp g_rew in d が不要であるためシンプルな手順です。
計画停止にてマスタとスレーブの役割を切り替えたレプリケーション構成図(1) マスタの正常停止
マスタにて実行します。

$ pg_ctl stop -m fast(2) スレーブの昇格
スレーブにて実行します。
以下の場合にはp g_rew in d 不要です。
計画停止
同期レプリケーションのインスタンス障害(物理障害なし）$ pg_ctl promote

以降の手順はフェイルオーバの場合と同様であるため省略します。

3. レプリケーションスロットの作成
4. reco very.co n f の作成
Page 46 of 285

PostgreSQL Enterprise Consortium

5. 旧マスタのp o stgresql.co n f の修正
6. 新スレーブの起動
7. 新マスタでのレプリケーション確認
8. レプリケーション方式を同期式に変更(9) レプリケーションスロットの削除(スレーブ）
フェイルオーバとの違いとしては、新スレーブに旧マスタ時代のスロットが残る事があります。
restart_lsn 列に値が残っている状態では、マスタのVACU U M 処理を阻害するなどの悪影響の可能性があるため、削除します。
スロットの削除は関数で行うため、スレーブでも実行可能です。

$ psql=# SELECT pg_drop_replication_slot('slot_server2');
pg_create_physical_replication_slot------------------------------------(1 row)=# SELECT slot_name FROM pg_replication_slots ;(0 rows)

以上でスイッチオーバが完了しました。

5.4.2.4. スイ ッチ バック
p g_rew in d を使用したスイッチバックについて記載します。
p g_rew in d はタイムラインのずれたレプリケーションを再同期させる機能です。 実行後、ターゲットクラスタはソースクラスタと置き換えられた状態になります。 そのため
p g_rew in d 後の操作は、通常のフェイルオーバ時と同じです。 タイムラインの分岐点からソースクラスタのW AL を適用するため、更新量が少なければp g_b aseb ack による複製より高速です。 これによりフェイルオーバ時、旧マスタを容易に新スレーブとして起動させることができます。
スイッチオーバー後に役割を元に戻したレプリケーション構成図■ 関連パラメータ
p g_rew in d に必要な設定を記載します。
表 5.20 p g_rew in d に必要な設定
サーバ

設定ファイ ルパラ メ ータ

設定値

内容

マスタ

p o stgresql.co n f

fu ll_p age_w riteson

チェックポイント後の更新時、ディスクページの全内容をW AL に書き込む。

マスタ

p o stgresql.co n fw al_lo g_h in ts

on

ヒントビット更新時もfu ll_p age_w rites を実行する。

■p g_rew in d コマンド

Page 47 of 285

PostgreSQL Enterprise Consortiump g_rew in d コマンドのの主なオプションは次の通りです。
表 5.21 p g_rew in d のオプション
オプショ ンD < ターゲットクラスタ >so u rce-server= "< ソースクラスタ > "内容

p g_rew in d を実行し、ソースクラスタの内容に置き換えるクラスタを指定する。
同期対象であるソースクラスタを指定します。主に次の接続文字列を使用します。

h o st ：ソースクラスタのホスト名またはIP アドレス
p o rt ：ソースクラスタのポート番号
db n ame ：ソースクラスタの接続先データベース名u ser ：ソースクラスタの接続先ユーザ
P

進行状況をレポートとして表示する。

■ スイッチバック手順※事前にマスタ /スレーブで(1) 関連パラメータの設定がされていることを前提とします。

(1) 旧マスタの正常停止
p g_rew in d を使用するには正常停止する必要があります。
停止した旧マスタを一旦起動した後、正常停止させます。
障害により正常に起動や停止ができない状態ではp g_rew in d は使用できません。
その場合は、p g_b aseb acku p を使用します。

$ pg_ctl start -w$ pg_ctl stop -m fast -w(2) p g_rew in d の実行
旧マスタでp g_rew in d を実行する。

$ pg_rewind -D $PGDATA --source-server="host=server2 port=5432"
servers diverged at WAL position 0/5015B70 on timeline 1
rewinding from last common checkpoint at 0/5015B00 on timeline 1
Done!

(3) スレーブのreco very.co n f を編集
旧マスタの＄PG DATA 配下に reco very.co n f を作成し、以下を設定します。

$ vi $PGDATA/recovery.conf[編集後]
standby_mode = 'on'
primary_conninfo = 'host=server2 port=5432 user=rep_user'
recovery_target_timeline = 'latest'(4) 新スレーブを起動し、新マスタとタイムラインID が揃っていることを確認する。
タイムラインID の取得には、p g_co n tro ldata コマンドを使用する。
厳密には最新チェックポイント実行時のタイムラインID であるため、タイムラインID が揃っていないときは、マスタにてチェックポイント実行後、再確認します。
ssh 経由で実行する事で任意のノードから全データベースクラスタの情報が取得できる。

Page 48 of 285

PostgreSQL Enterprise Consortium[新マスタ ]
$ export LANG=C$ pg_controldata | grep " TimeLineID"
Latest checkpoint's TimeLineID:2$ ssh server1 $PGHOME/bin/pg_controldata $PGDATA | grep " TimeLineID"
Latest checkpoint's TimeLineID:2■p g_rew in d 使用時の注意点
正常停止が必要p g_rew in d を実行するデータベースクラスタは正常終了しなければいけません。
物理障害等により正常停止できない場合、p g_rew in d は使用できません。p g_b aseb acku p を使用します。
同一タイムラインの場合は実施不可

p g_p ro mo te を実行せずに旧スレーブを新マスタにした場合、新マスタのタイムラインID は変わらないため、新マスタと旧マスタのタイムラインID は同じ状態です。
この場合は、p g_rew in d は実行できません。
実行時期と所要時間の関係

p g_rew in d によるフェイルバックの所要時間は２つの要素から構成されます。
Step 1. p g_rew in d による巻き戻し （旧マスタのW AL を使用）
Step 2. W AL 適用による追い付き （新マスタのW AL を使用）
新マスタで大量更新がある場合は、Step 1 は短時間で終了してもStep 2 で時間がかかります。
結果として、p g_b aseb acku p の方が効率が良い場合もあり得ます。
また新マスタ昇格時のW AL が削除されている場合は、後述するように Step 2 でエラーとなる可能性もあります。その場合はp g_b aseb acku pが必要となります。
p g_rew in d はフェイルオーバー後、あまり時間を置かずに実行する事がポイントです。
旧マスタのW AL 削除
旧マスタの巻き戻しに必要な旧マスタのW AL が削除されているいる場合、p g_rew in d は失敗します。
例えば旧マスタが障害により大量更新の途中で異常終了した場合などに発生します。
p g_rew in d 実行時に次のようなエラーが発生します。

could not open file "/home/pg96/pg96_data/pg_xlog/0000000D00000002000000CF": No
such file or directory
could not find previous WAL record at 2/CF000140
Failure, exiting
p g_rew in d が成功するかどうかは検証(dry-ru n オプション)にて事前に確認する事ができます。

$ pg_rewind -D $PGDATA --source-server="host=server2 port=5432" --dry-run
servers diverged at WAL position 0/5015B70 on timeline 1
rewinding from last common checkpoint at 0/5015B00 on timeline 1
Done!
| メッセージはdry-runオプションが無い場合と同じです。
| pg_rewindでエラーが発生する場合(スレーブのWAL削除）は、この検証にて確認できますが、| pg_rewindでエラーが発生しないで、後から発生する場合（マスタのWAL削除）は検知できません。
| 検証の仕様について認識下さい。

新マスタのW AL 削除

p g_rew in d 後、新スレーブは新マスタのW AL を適用することで、新マスタと同期します。
新マスタに昇格時のW AL ファイルが残っていない場合、新スレーブは追い付きができず、次のエラーがサーバログに出力され続けます。

Page 49 of 285

PostgreSQL Enterprise Consortium対策としてレプリケーションスロットの有効化が有力ですが、新マスタのW AL 領域の枯渇にご注意下さい。

ERROR: requested WAL segment 0000000D00000000000000F3 has already been removed

タイムラインの巻き戻し

p g_rew in d はPo stgreSQ L9.6 からタイムラインの巻き戻しができるよになっています。
これによりスプリットブレインが発生しても、新マスタをスレーブに戻すことが可能です。

p g_rew in d が不要な場合
p g_rew in d はターゲットとソースクラスタのタイムラインID が分岐した場合に実行が必要です。
そのためタイムラインが枝分かれしなかった場合、p g_rew in d を実行する必要はありません。
例えばp g_rew in d 実行時に次のようなメッセージが出た場合、p g_rew in d は実行せずに、以降の操作を継続します。

servers diverged at WAL position 0/503A428 on timeline 2
no rewind required

5.4.2.5. スレーブ障害に よる対処
スレーブ障害時の緊急対応の必要性は、非同期モードが同期モードかによって異なります。
非同期モードの場合は、スレーブ障害がマスタの更新処理を阻害しないため、緊急対応は必要ありません。
とはいえ、シングル状態であるため早期にレプリケーション構成に復旧します。
同期モードの場合は、スレーブ障害に より マスタ の更新処理が阻害さ れ ハング状態と なり ます 。

$ psql -h server1 -U postgres postgres -c "INSERT INTO test1_t VALUES ( 1 )"Cancel request sent
WARNING: canceling wait for synchronous replication due to user request
DETAIL: ** The transaction has already committed locally, but might not have been replicated tothe standby. **INSERT 0 1※Ctrl+Cをキーインする等、意図的にキャンセルしない限り、応答が返ってきません。
そのため、直ちに非同期に設定する必要があります。
非同期への切り替え処理は以降に示す様に再起動不要であるため、即時対応が可能です。
非同期への切り替えではなく、スレーブの再起動を試みる方法も考えられます。その方が効率的のようにも考えられます。
即座に起動できればその通りですが、起動に時間がかかる、あるいは物理的な障害で起動できない状態である事も考えられます。
そのような試行錯誤より、まずは確実にマスタのハング状態解消を優先します。
マスタの復旧後は、スレーブの復旧を試みます。
物理障害により起動できない場合は、フェイルバック処理と同様の作業を行います。

(1) スレーブの疑似障害発生
immediate オプションにて停止、またはp o stgres プロセスのkill する事で擬似障害を発生させます。
スレーブにて実行します。

$ pg_ctl -w -m immediate stop(2) スレーブの死活監視にて異常を検知
死活監視の方法には多数ありますが、ここではp g_isreadyコマンドを使用します。

Page 50 of 285

PostgreSQL Enterprise Consortiump g_isreadyコマンドはクライアントツールですので、任意のノードから実行できます。

$ pg_isready -h server2 -U postgres -d postgres
server2:5432 - no response(3) 非同期モードに切り替え
syn ch ro n o u s_stan db y_n ames パラメータを'' に設定する事で、非同期の設定となります。

$ vi $PGDATA/postgresql.conf[変更前]
synchronous_standby_names = '*'[変更後]
synchronous_standby_names = ''$ pg_ctl reload(4) 非同期モードの確認
レプリケーションモードが非同期(asyn c)に変更された事を確認します。

$ psql -At -c "SELECT sync_state FROM pg_stat_replication;"
async
これでマスタが更新処理が可能な状態に復旧しました。 ただしシングル状態であるため、フェイルオーバと同様の作業を行います。

5.4.3. 障害時運用手順（ 複数スレー ブ）■ 同期モードについてスレーブが複数ある場合、障害時にどのスレーブを新マスタに昇格するかが重要になります。 基本的には同期運用しているスレーブを昇格すべきですが、同期対象も複数選べるため、どの同期スレーブを優先するかが問題です。
これに対しPo stgreSQ L は、通常の同期運用では、同期優先度を明確にすることで、昇格すべきスレーブを絞っています。 しかし同期優先度が明確な場合、同期対象ス
レーブの性能に、マスタ側も大きく影響を受けます。 そのため複数同期においても、最も処理が進んでいるスレーブを同期対象とすることでスレーブの影響を抑えるクォーラム
コミットという機能が用意されています。
ただしクォーラムコミットの場合、最も同期が進んでいるスレーブがその時によって異なるため、運用に工夫が必要です。 性能面と運用面のトレードオフを考慮した上で、同期モードを選択する必要があります。
通常の同期

synchronous_standby_names = 'FIRST X (standby_name1, standby_name2, ...)'
通常の同期モードで運用する場合、上記のように syn ch ro n o u s_stan db y_n ames を設定します。 FIRSTは通常の同期モードの選択であり、省略可能です。 数字Xは同期対象のスレーブ数であり、カッコ内の左から順に優先して同期対象になります。 カッコ内のスレーブ名は、同期候補のスレーブです。
以下のような設定の場合、slave1 ～3 が同期運用、slave4 ～5 は潜在的な同期運用がされます。 潜在的な同期運用は、普段は非同期として運用され、必要な場合は同期へ昇格されます。 例えばslave2 の環境がマスタから切断された場合、残ったスレーブで優先度が高いslave1,slave3,slave4 が同期運用されます。 (つまり非同期運用であった slave4 が同期運用へ昇格されます)

synchronous_standby_names = 'FIRST 3 (slave1, slave2, slave3, slave4, slave5)'
障害発生時は、同期の優先度が高いスレーブを新マスタへ昇格させます。 通常の同期モードではカッコ内の左側が優先度が高く、右に行くほど優先度が低くなります。
クォーラムコミット

synchronous_standby_names = 'ANY X (standby_name1, standby_name2, ...)'
クォーラムコミットで運用する場合、上記のように syn ch ro n o u s_stan db y_n ames を設定します。 ANYはクォーラムコミットの選択であり、こちらは省略できません。 数字Xは同期対象のスレーブ数であり、カッコ内のうちX台のスレーブの同期を待ちます。 カッコ内のスレーブ名は、同期候補のスレーブです。
以下のような設定の場合、slave1 ～5 の全てがクォーラムコミットで運用されます。 マスタ側で更新があった場合、slave1 ～5 のうちいずれか計3 台でマスタからの更新反映

Page 51 of 285

PostgreSQL Enterprise Consortiumが完了すれば、同期したとみなします。

synchronous_standby_names = 'ANY 3 (slave1, slave2, slave3, slave4, slave5)'
障害発生時、新マスタへ昇格させるスレーブを選択するのには注意が必要です。 確認方法は「 SELECT p g_co n tro l_reco very(); 」コマンドにより現在のW AL 位置を比較することです。 同期が進んでいる側はW AL も進んでいます。これにより判断が可能です。

■ 前提
以降の手順では次の前提とします。

Po stgreSQ L 10
マスタ、2 台スレーブの計3 台構成（ホスト名をそれぞれ server1,server2,slave3 と表記する）
同期モードは同期（ syn ch ro n o u s_co mmit = o n または remo te_ap p ly)
同期にはクォーラムコミットを使用（ syn ch ro n o u s_stan db y_n ames = 'ANY 1 (slave_server2, slave_server3)' ）スレーブはホットスタンバイ機能により参照可能 (h o t_stan db y = o n )レプリケーション用のユーザは rep _u ser
マスタ /スレーブとも、p o rt は5432 を使用
レプリケーションスロット使用
マスタ /スレーブとも、環境変数PG DATA,PG PO RTは設定済み
死活監視は実際の運用ではクラスタソフトを使用するのが一般的だが、ここでは便宜上手動で実施
サーバのNIC はパブリックのみ
仮想IP については考慮しない■ 対処一覧 大別すると4 種類の対処方法が考えられます。
表 5.22 状況別の対処

ID

障害箇
所

1

マスタ

障害状況

対処

マスタとスレーブの関係が崩れており再構成が必要

p g_b aseb acku p コマンドを使用してフェイルバック

物理障害

p ro mo te を伴わないスレーブのマスタ化(reco very.co n f 削除）
2

マスタ

マスタとスレーブの関係は巻き戻しで復旧可能

p g_rew in d コマンドを使用してスイッチバック

非同期レプリケーションのインスタンス障害（差異がある状態で昇格）
スプリットブレイン状態での旧マスタへの更新

3

マスタ

マスタとスレーブの切り替え可能

p g_rew in d コマンドを使用しないでスイッチバック

計画停止
同期レプリケーションのインスタンス障害(物理障害なし）

4

スレーブ

マスタとスレーブの連携再開可能

同期式の場合、残ったスレーブを非同期式に切り替え

スレーブの障害

5.4.3.1. フェ イ ルオーバ ( 複数スレーブ )フェイルオーバについて記載します。
次のような状況を想定しています。

server1: マスタ → 障害により停止
server2: スレーブ → 新マスタへ昇格
server3: スレーブ → マスタからのW AL 転送が途絶えたたえめ、更新が停止
マスタにて、障害が発生した場合のスレーブを新マスタへ昇格したシングル構成図

Page 52 of 285

PostgreSQL Enterprise Consortium(1) マスタの疑似障害発生
immediate オプションにて停止、またはp o stgres プロセスのkill する事で擬似障害を発生させます。
マスタ (server1)にて実行します。

$ pg_ctl -w -m immediate stop

$ kill -9 `head -1 $PGDATA/postmaster.pid`(2) マスタの死活監視にて異常を検知
死活監視の方法には多数ありますが、ここではp g_isreadyコマンドを使用します。
p g_isreadyコマンドはクライアントツールですので、任意のノードから実行できます。

$ pg_isready -h server1 -U postgres -d postgres
server1:5432 - no response(3) 昇格対象のスレーブ決定
クォーラムコミットの場合、現在のW AL 位置を確認し、新マスタへ昇格すべきスレーブを特定します。
両方のスレーブ(server1,slave2)にて実行し、結果を比較します。
※通常の同期モードでは、優先度の高いスレーブが昇格対象なため、本操作は必要ありません。

[server2]=# SELECT pg_control_recovery();pg_control_recovery-------------------------(0/1301F348,1,0/0,0/0,f)(1 row)[server3]=# SELECT pg_control_recovery();pg_control_recovery-------------------------(0/1301F170,1,0/0,0/0,f)(1 row)※WALはserver2が進んでいるため、server2を昇格させる。

server1:1301F348
server2:1301F170(4) スレーブを新マスタへ昇格
旧スレーブ(server2)で実行します。

$ pg_ctl promote※サーバログに下記内容が記載されること"datab ase system is ready to accep t co n n ectio n s"
ただしこの時点ではsyn ch ro n o u s_stan db y_n ames パラメータに値が設定されている場合、新マスタで更新処理ができない状態です。

(5) 新マスタを非同期に切り替え
syn ch ro n o u s_stan db y_n ames パラメータの設定を'' に設定し、リロードで反映します。
新マスタ (server2)で実行します。

Page 53 of 285

PostgreSQL Enterprise Consortium$ vi $PGDATA/postgresql.conf[編集後]
synchronous_standby_names = ''$ pg_ctl reload
これで更新処理ができる状態になりました。

(6) 新マスタの死活監視にて正常を確認$ pg_isready -h server2 -U postgres -d postgres
server2:5432 - accepting connections
以上でファイルオーバーは完了です。

5.4.3.2. フェ イ ルバック ( 複数スレーブ )p g_b aseb acku p を使用したフェイルバックについて記載します。 昇格されなかったスレーブは、新マスタのスレーブとして運用するために再設定が必要です。 旧マスタから
新スレーブへの構築は初期構築手順とほぼ同じです。
マスタ障害発生によるフェイルオーバ後、旧マスタを新スレーブとしたレプリケーション構成図■ パラメータp g_b aseb acku p に必要な設定を記載します。
表 5.23 p g_b aseb acku p に必要な設定
サーバ

設定ファイ ルパラ メ ータ

設定値

内容

マスタ

p o stgresql.co n f listen _address

0.0.0.0

全てのIP アドレス(v4)からの接続を受け付ける

マスタ

p o stgresql.co n f max_w al_sen ders2

W AL ストリームオプションを付与する場合は、2 以上を設定■p g_b aseb acku p コマンドp g_b aseb acku p コマンドの主なオプションは次の通りです。

Page 54 of 285

PostgreSQL Enterprise Consortium
表 5.24 p g_b aseb acku p のオプション
オプショ ン

内容-D < directo ry>

出力を書き出すディレクトリを指定。

-X < meth o d>--xlo g-meth o d= < meth o d>

必要なW AL ファイルをバックアップに含める。meth o d(収集方式)は以下から選択。

fetch ： W AL ファイルは最後に収集
stream ：バックアップ作成中に同時に W AL をストリームで収集運用中に p g_b aseb acku p を実行する場合には stream を指定する。
fetch （最後に収集）では、必要なW AL ファイルが削除される可能性があるため。

-S < slo t_n ame>--slo t= < slo t_n ame>

W AL ストリーミングの収集に指定したレプリケーションスロットを使用。
-X stream とセットで指定。
必要なW AL ファイルが削除されるのを防ぐ事を目的とする。
運用中に p g_b aseb acku p を実行する場合に使用を検討する。
以下に注意する。
事前にレプリケーションスロットを作成する必要がある
マスタのW AL 領域の空きが十分である事を確認する-R--w rite-reco very-co n f

最低限のreco very.co n f を作成。必要に応じて reco very.co n f を加筆修正。

-r < rate>--max-rate= < rate>

サーバから転送されるデータの最大転送速度を指定。
運用中に p g_b aseb acku p を実行する場合に使用を検討する。
転送速度を抑える事で、マスタに対する影響を制限する事を目的とする。

-P

進行状況報告を有効化。
p g_b aseb acku p 処理中におおよその進行状況を報告する。
運用中に実行した場合、データベースクラスタのサイズが増加して進行状況が100% を超える場合がある。

-v

冗長モードを有効化。
進行状況報告も有効な場合、現在処理中のファイル名を出力。

p g_b aseb acku p でレプリケーションスロットが使用できます。 W AL 収集方式に stream を指定する事でW AL をほぼ確保できますが、スロットを指定する事でより確実になります。 スロットを使用する運用であれば、この段階で作成するのが有力です。

■ フェイルバック手順
次のような状況を想定しています。

server1: 旧マスタ → 新スレーブ
server2: 旧スレーブ → 新マスタserver3: 旧スレーブ → 新スレーブ
p g_b aseb acku p にスロットを指定する場合を記載します。
(1) レプリケーションスロットの作成
新マスタ (server2)にて実行します。

$ psql[server1:旧マスタ →新スレーブ用のレプリケーションスロット]=# SELECT pg_create_physical_replication_slot('slot_server1',true); -- 第2パラメータに trueを指定
pg_create_physical_replication_slot------------------------------------(slot_server1,144/EEFC8940)(1 行)[server3:旧スレーブ→新スレーブ用のレプリケーションスロット]=# SELECT pg_create_physical_replication_slot('slot_server3',true); -- 第2パラメータに trueを指定
pg_create_physical_replication_slot------------------------------------(slot_server3,144/EEFC8940)(1 行)

Page 55 of 285

PostgreSQL Enterprise Consortium(2) reco very.co n f の修正
旧スレーブ(server3)にて実行します。p rimary_co n n in fo に記載する接続先マスタの情報を新マスタ (server2)に書き換えます。
またレプリケーションスロット名が変更されている場合、変更後の名前に合わせます。

$ vi $PGDATA/recovery.conf[編集前]
primary_conninfo = 'user=rep_user host=server1 port=5432 application_name=slave_server3
asslmode=prefer sslcompression=1'[編集前]
primary_conninfo = 'user=rep_user host=server2 port=5432 application_name=slave_server3
asslmode=prefer sslcompression=1'(3) 新スレーブの起動
旧スレーブ(server3)にて実行します。reco very.co n f 修正反映し、新スレーブとして再起動します。

$ pg_ctl restart(4) 旧マスタのデータベースクラスタを削除
旧マスタ (server1)にて実行します。

$ rm -rf $PGDATA/*※$PG DATA 以外に表領域を作成している場合、そのファイルも削除します。
※$PG DATA ディレクトリを削除する場合は、p o stgres ユーザで$PG DATA ディレクトリを作成できるよう
親ディレクトリのオーナーまたはパーミッションを設定します。
(5) p g_b aseb acku p コマンドにて、新マスタからデータベースクラスタをコピー
旧マスタ (server1)にて実行します。

$ pg_basebackup -h server2 -U rep_user -D $PGDATA -X stream -S slot_server1 -P -v -Rtransaction log start point: 144/F3000028 on timeline 17
pg_basebackup: starting background WAL receiver
10503022/10503022 kB (100%), 1/1 tablespacetransaction log end point: 144/F3000130
pg_basebackup: waiting for background process to finish streaming ...
pg_basebackup: base backup completed(6) reco very.co n f の修正
旧マスタ (server1)にて実行します。
p g_b aseb acku p にて -R オプションを指定した事で、reco very.co n f が作成されます。
スロットを指定している事から、p rimary_slo t_n ame の指定があります。以下を追記します。
reco very_target_timelin e パラメータp rimary_co n n in f パラメータに ap p licatio n _n ame を追加（任意／デフォルトはw alreceiver ）$ vi $PGDATA/recovery.conf[編集前]
standby_mode = 'on'
primary_conninfo = 'user=rep_user host=server2 port=5432 sslmode=prefer sslcompression=1'
primary_slot_name = 'slot_server1'[編集後]
standby_mode = 'on'
primary_conninfo = 'user=rep_user host=server2 port=5432 application_name=slave_server1
asslmode=prefer sslcompression=1'
primary_slot_name = 'slot_server1'
recovery_target_timeline = latest(7) 旧マスタのp o stgresql.co n f の修正

Page 56 of 285

PostgreSQL Enterprise Consortium
旧マスタ (server1)にて、パラメータの調整を行います。

syn ch ro n o u s_stan db y_n ames パラメータの無効化
p g_statsin fo の無効化(有効化されている場合）
同期レプリケーションの設定を無効化します。
新スレーブのsh ared_p relo ad_lib raries パラメータに p g_statsin fo が設定されている場合は、書き込みができずにエラーが発生します。
p g_statsin fo の指定を削除します。

$ vi $PGDATA/postgresql.conf[編集後]
synchronous_standby_names = ''shared_preload_libraries = ''(6) 新スレーブの起動
新スレーブを起動します。

$ pg_ctl start(7) 新マスタでのレプリケーション確認
p g_stat_rep licatio n s ビューを参照して、レプリケーション構成である事を確認します。
p g_rep licatio n _slo ts ビューを参照して、スロットがアクティブである事を確認します。

Page 57 of 285

PostgreSQL Enterprise Consortium$ psql=# SELECT * FROM pg_stat_replication;-[ RECORD 1 ]----+-----------------------------pid| 1866
usesysid| 16384
usename| rep_user
application_name | slave_server3
client_addr| ::1client_hostname |client_port| 46840
backend_start| 2018-02-13 14:06:04.475996+09
backend_xmin|
state| streaming--- ストリーミング中
sent_lsn| 0/B067570
write_lsn| 0/B067570
flush_lsn| 0/B067570
replay_lsn| 0/B067570
write_lag|
flush_lag|
replay_lag|
sync_priority| 0
sync_state| async--- 非同期-[ RECORD 2 ]----+-----------------------------pid| 1776
usesysid| 16384
usename| rep_user
application_name | slave_server1
client_addr| ::1client_hostname |client_port| 46838
backend_start| 2018-02-13 14:05:53.580727+09
backend_xmin|
state| streaming--- ストリーミング中
sent_lsn| 0/B067570
write_lsn| 0/B067570
flush_lsn| 0/B067570
replay_lsn| 0/B067570
write_lag|
flush_lag|
replay_lag|
sync_priority| 0
sync_state| async--- 非同期=# SELECT slot_name,active FROM pg_replication_slots;-[ RECORD 1 ]---slot_name | slot1
active| t--- アクティブ-[ RECORD 2 ]---slot_name | slot2
active| t--- アクティブ(8) レプリケーション方式を同期式に変更
新マスタにて、syn ch ro n o u s_stan db y_n ames パラメータを設定し、リロードで反映します。

$ vi $PGDATA/postgresql.conf[編集前]
synchronous_standby_names = ''[編集後]
synchronous_standby_names = 'ANY 1 (slave_server1, slave_server3)'$ pg_ctl reload

Page 58 of 285

PostgreSQL Enterprise Consortium$ psql -h server2 -U postgres postgres -c "SELECT * FROM pg_stat_replication" -x-[ RECORD 1 ]----+-----------------------------pid| 1866
usesysid| 16384
usename| rep_user
application_name | slave2
client_addr| ::1client_hostname |client_port| 46840
backend_start| 2018-02-13 14:06:04.475996+09
backend_xmin|
state| streaming--- ストリーミング中
sent_lsn| 0/B067570
write_lsn| 0/B067570
flush_lsn| 0/B067570
replay_lsn| 0/B067570
write_lag|
flush_lag|
replay_lag|
sync_priority| 1
sync_state| quorum--- クォーラムコミット-[ RECORD 2 ]----+-----------------------------pid| 1776
usesysid| 16384
usename| rep_user
application_name | slave1
client_addr| ::1client_hostname |client_port| 46838
backend_start| 2018-02-13 14:05:53.580727+09
backend_xmin|
state| streaming--- ストリーミング中
sent_lsn| 0/B067570
write_lsn| 0/B067570
flush_lsn| 0/B067570
replay_lsn| 0/B067570
write_lag|
flush_lag|
replay_lag|
sync_priority| 1
sync_state| quorum--- クォーラムコミット
これにて、以下の構成に復旧しました。
レプリケーションスロット使用
同期式レプリケーション構成

5.4.3.3. スイ ッチ オーバ ( 複数スレーブ )スイッチオーバについて記載します。

■ スイッチオーバ手順
計画停止におけるマスタ /スレーブの切り替え手順です。 p g_b aseb acku p やp g_rew in d が不要であるためシンプルな手順です。
次のような状況を想定しています。

server1: マスタ → 新スレーブ
server2: スレーブ → 新マスタserver3: スレーブ → 新スレーブ
計画停止にてマスタとスレーブの役割を切り替えたレプリケーション構成図

Page 59 of 285

PostgreSQL Enterprise Consortium(1) マスタの正常停止
マスタ (server1)にて実行します。

$ pg_ctl stop -m fast(2) スレーブの昇格
スレーブ(server2)にて実行します。
以下の場合にはp g_rew in d 不要です。
計画停止
同期レプリケーションのインスタンス障害(物理障害なし）$ pg_ctl promote

以降の手順はフェイルオーバの場合と同様であるため省略します。

3. レプリケーションスロットの作成
4. reco very.co n f の作成
5. 旧マスタのp o stgresql.co n f の修正
6. 新スレーブの起動
7. 新マスタでのレプリケーション確認
8. レプリケーション方式を同期式に変更(9) レプリケーションスロットの削除(スレーブ）
フェイルオーバとの違いとしては、新スレーブ(server1)に旧マスタ時代のスロットが残る事があります。
restart_lsn 列に値が残っている状態では、マスタのVACU U M 処理を阻害するなどの悪影響の可能性があるため、削除します。
スロットの削除は関数で行うため、スレーブでも実行可能です。

$ psql=# SELECT pg_drop_replication_slot('slot_server2');
pg_create_physical_replication_slot------------------------------------(1 row)=# SELECT pg_drop_replication_slot('slot_server3');
pg_create_physical_replication_slot------------------------------------(1 row)=# SELECT slot_name FROM pg_replication_slots ;(0 rows)

以上でスイッチオーバが完了しました。

5.4.3.4. スイ ッチ バック ( 複数スレーブ )Page 60 of 285

PostgreSQL Enterprise Consortiump g_rew in d を使用したスイッチバックについて記載します。
p g_rew in d はタイムラインのずれたレプリケーションを再同期させる機能です。 実行後、ターゲットクラスタはソースクラスタと置き換えられた状態になります。 そのため
p g_rew in d 後の操作は、通常のフェイルオーバ時と同じです。 タイムラインの分岐点からソースクラスタのW AL を適用するため、更新量が少なければp g_b aseb acku p による複製より高速です。 これによりフェイルオーバ時、旧マスタを容易に新スレーブとして起動させることができます。
スイッチオーバー後に役割を元に戻したレプリケーション構成図■ 関連パラメータ
p g_rew in d に必要な設定を記載します。
表 5.25 p g_rew in d に必要な設定
サーバ

設定ファイ ルパラ メ ータ

設定値

内容

マスタ

p o stgresql.co n f

fu ll_p age_w riteson

チェックポイント後の更新時、ディスクページの全内容をW AL に書き込む。

マスタ

p o stgresql.co n fw al_lo g_h in ts

on

ヒントビット更新時もfu ll_p age_w rites を実行する。

■p g_rew in d コマンドp g_rew in d コマンドのの主なオプションは次の通りです。
表 5.26 p g_rew in d のオプション
オプショ ンD < ターゲットクラスタ >so u rce-server= "< ソースクラスタ > "内容

p g_rew in d を実行し、ソースクラスタの内容に置き換えるクラスタを指定する。
同期対象であるソースクラスタを指定します。主に次の接続文字列を使用します。

h o st ：ソースクラスタのホスト名またはIP アドレス
p o rt ：ソースクラスタのポート番号
db n ame ：ソースクラスタの接続先データベース名u ser ：ソースクラスタの接続先ユーザ
P

進行状況をレポートとして表示する。

■ スイッチバック手順※事前にマスタ /スレーブで(1) 関連パラメータの設定がされていることを前提とします。

(1) 旧マスタの正常停止
p g_rew in d を使用するには正常停止する必要があります。
停止した旧マスタを一旦起動した後、正常停止させます。
障害により正常に起動や停止ができない状態ではp g_rew in d は使用できません。
その場合は、p g_b aseb acku p を使用します。

Page 61 of 285

PostgreSQL Enterprise Consortium$ pg_ctl start -w$ pg_ctl stop -m fast -w(2) p g_rew in d の実行
旧マスタでp g_rew in d を実行する。

$ pg_rewind -D $PGDATA --source-server="host=server2 port=5432"
servers diverged at WAL position 0/5015B70 on timeline 1
rewinding from last common checkpoint at 0/5015B00 on timeline 1
Done!

(3) スレーブのreco very.co n f を編集
旧マスタの＄PG DATA 配下に reco very.co n f を作成し、以下を設定します。

$ vi $PGDATA/recovery.conf[編集後]
standby_mode = 'on'
primary_conninfo = 'host=server2 port=5432 user=rep_user'
recovery_target_timeline = 'latest'(4) 新スレーブを起動し、新マスタとタイムラインID が揃っていることを確認する。
タイムラインID の取得には、p g_co n tro ldata コマンドを使用する。
厳密には最新チェックポイント実行時のタイムラインID であるため、タイムラインID が揃っていないときは、マスタにてチェックポイント実行後、再確認します。
ssh 経由で実行する事で任意のノードから全データベースクラスタの情報が取得できる。

[新マスタ ]
$ export LANG=C$ pg_controldata | grep " TimeLineID"
Latest checkpoint's TimeLineID:2$ ssh server1 $PGHOME/bin/pg_controldata $PGDATA | grep " TimeLineID"
Latest checkpoint's TimeLineID:2■p g_rew in d 使用時の注意点
正常停止が必要p g_rew in d を実行するデータベースクラスタは正常終了しなければいけません。
物理障害等により正常停止できない場合、p g_rew in d は使用できません。p g_b aseb acku p を使用します。
同一タイムラインの場合は実施不可

p g_p ro mo te を実行せずに旧スレーブを新マスタにした場合、新マスタのタイムラインID は変わらないため、新マスタと旧マスタのタイムラインID は同じ状態です。
この場合は、p g_rew in d は実行できません。
実行時期と所要時間の関係

p g_rew in d によるフェイルバックの所要時間は２つの要素から構成されます。
Step 1. p g_rew in d による巻き戻し （旧マスタのW AL を使用）
Step 2. W AL 適用による追い付き （新マスタのW AL を使用）

Page 62 of 285

PostgreSQL Enterprise Consortium
新マスタで大量更新がある場合は、Step 1 は短時間で終了してもStep 2 で時間がかかります。
結果として、p g_b aseb acku p の方が効率が良い場合もあり得ます。
また新マスタ昇格時のW AL が削除されている場合は、後述するように Step 2 でエラーとなる可能性もあります。その場合はp g_b aseb acku pが必要となります。
p g_rew in d はフェイルオーバー後、あまり時間を置かずに実行する事がポイントです。
旧マスタのW AL 削除
旧マスタの巻き戻しに必要な旧マスタのW AL が削除されているいる場合、p g_rew in d は失敗します。
例えば旧マスタが障害により大量更新の途中で異常終了した場合などに発生します。
p g_rew in d 実行時に次のようなエラーが発生します。

could not open file "/home/pg96/pg96_data/pg_xlog/0000000D00000002000000CF": No
such file or directory
could not find previous WAL record at 2/CF000140
Failure, exiting
p g_rew in d が成功するかどうかは検証(dry-ru n オプション)にて事前に確認する事ができます。

$ pg_rewind -D $PGDATA --source-server="host=server2 port=5432" --dry-run
servers diverged at WAL position 0/5015B70 on timeline 1
rewinding from last common checkpoint at 0/5015B00 on timeline 1
Done!
| メッセージはdry-runオプションが無い場合と同じです。
| pg_rewindでエラーが発生する場合(スレーブのWAL削除）は、この検証にて確認できますが、| pg_rewindでエラーが発生しないで、後から発生する場合（マスタのWAL削除）は検知できません。
| 検証の仕様について認識下さい。

新マスタのW AL 削除

p g_rew in d 後、新スレーブは新マスタのW AL を適用することで、新マスタと同期します。
新マスタに昇格時のW AL ファイルが残っていない場合、新スレーブは追い付きができず、次のエラーがサーバログに出力され続けます。
対策としてレプリケーションスロットの有効化が有力ですが、新マスタのW AL 領域の枯渇にご注意下さい。

ERROR: requested WAL segment 0000000D00000000000000F3 has already been removed

タイムラインの巻き戻し

p g_rew in d はPo stgreSQ L9.6 からタイムラインの巻き戻しができるよになっています。
これによりスプリットブレインが発生しても、新マスタをスレーブに戻すことが可能です。

p g_rew in d が不要な場合
p g_rew in d はターゲットとソースクラスタのタイムラインID が分岐した場合に実行が必要です。
そのためタイムラインが枝分かれしなかった場合、p g_rew in d を実行する必要はありません。
例えばp g_rew in d 実行時に次のようなメッセージが出た場合、p g_rew in d は実行せずに、以降の操作を継続します。

servers diverged at WAL position 0/503A428 on timeline 2
no rewind required

5.4.3.5. スレーブ障害に よる対処 ( 複数スレーブ )スレーブ障害時の緊急対応の必要性は、非同期モードが同期モードかによって異なります。
非同期モードの場合は、スレーブ障害がマスタの更新処理を阻害しないため、緊急対応は必要ありません。
とはいえ、シングル状態であるため早期にレプリケーション構成に復旧します。
同期モードの場合、稼働しているスレーブ数が、同期対象のスレーブ数より下回った場合、スレーブ障害に より マスタ の更新処理が阻害さ れ ハング状態と なり ます 。

Page 63 of 285

PostgreSQL Enterprise Consortium$ psql -h server1 -U postgres postgres -c "INSERT INTO test1_t VALUES ( 1 )"Cancel request sent
WARNING: canceling wait for synchronous replication due to user request
DETAIL: ** The transaction has already committed locally, but might not have been replicated tothe standby. **INSERT 0 1※Ctrl+Cをキーインする等、意図的にキャンセルしない限り、応答が返ってきません。
そのため、直ちに非同期に設定する、または同期対象のスレーブ数設定を減らす必要があります。
非同期への切り替え処理は以降に示す様に再起動不要であるため、即時対応が可能です。
非同期への切り替えではなく、スレーブの再起動を試みる方法も考えられます。その方が効率的のようにも考えられます。
即座に起動できればその通りですが、起動に時間がかかる、あるいは物理的な障害で起動できない状態である事も考えられます。
そのような試行錯誤より、まずは確実にマスタのハング状態解消を優先します。
マスタの復旧後は、スレーブの復旧を試みます。
物理障害により起動できない場合は、フェイルバック処理と同様の作業を行います。

(1) スレーブの疑似障害発生
immediate オプションにて停止、またはp o stgres プロセスのkill する事で擬似障害を発生させます。
スレーブにて実行します。

$ pg_ctl -w -m immediate stop(2) スレーブの死活監視にて異常を検知
死活監視の方法には多数ありますが、ここではp g_isreadyコマンドを使用します。
p g_isreadyコマンドはクライアントツールですので、任意のノードから実行できます。

$ pg_isready -h server2 -U postgres -d postgres
server2:5432 - no response(3) 非同期モードに切り替え
syn ch ro n o u s_stan db y_n ames パラメータを'' に設定する事で、非同期の設定となります。

$ vi $PGDATA/postgresql.conf[変更前]
synchronous_standby_names = 'FIRST X (standby_name1, standby_name2, ...)'[変更後]
synchronous_standby_names = ''$ pg_ctl reload(4) 非同期モードの確認
レプリケーションモードが非同期(asyn c)に変更された事を確認します。

$ psql -At -c "SELECT sync_state FROM pg_stat_replication;"
async
これでマスタが更新処理が可能な状態に復旧しました。 ただしシングル状態であるため、フェイルオーバと同様の作業を行います。

Page 64 of 285

PostgreSQL Enterprise Consortium

5.4.4. 障害時運用手順（ カスケー ド構成）
■ 前提 以降の手順では次の前提とします。
Po stgreSQ L 10.1
マスタ、スレーブ1 、スレーブ2 の3 台のカスケード構成（ホスト名をそれぞれ server1,server2,server3 と表記する）
スレーブ1 が同期モード（ syn ch ro n o u s_co mmit= o n , syn ch ro n o u s_stan db y_n ames= 'server2')
スレーブ2 が非同期モード（ syn ch ro n o u s_co mmit= o ff, syn ch ro n o u s_stan db y_n ames= '' ）スレーブはホットスタンバイ機能により参照可能 (h o t_stan db y = o n )レプリケーション用のユーザは rep u ser
マスタ /スレーブとも、p o rt は5432 を使用
レプリケーションスロット使用
マスタ /スレーブとも、環境変数PG DATA,PG PO RTは設定済み
死活監視は実際の運用ではクラスタソフトを使用するのが一般的ですが、ここでは便宜上手動で実施
サーバのNIC はパブリックのみ
仮想IP については考慮しない■ 対処一覧 大別すると3 種類の対処方法が考えられます。
表 5.27 障害別の対処

ID

障害箇
所

1

マスタ

障害状況

pg_basebackup と pg_rewind の使い分け

マスタとスレーブの関係が崩れており再構成が必要

p g_b aseb acku p コマンドを使用してフェイルバック

物理障害

p ro mo te を伴わないスレーブのマスタ化(reco very.co n f 削除）
2

マスタ

マスタとスレーブの関係は巻き戻しで復旧可能

p g_rew in d コマンドを使用してスイッチバック

非同期レプリケーションのインスタンス障害（差異がある状態で昇格）
スプリットブレイン状態での旧マスタへの更新

3

マスタ

マスタとスレーブの切り替え可能

p g_rew in d コマンドを使用しないでスイッチバック

計画停止
同期レプリケーションのインスタンス障害(物理障害なし）

4

スレーブ1

マスタとスレーブの連携再開可能

同期式の場合は非同期式に切り替え

スレーブの障害5

スレーブ1

マスタとスレーブの連携再開不可能

p g_b aseb acku p コマンドを使用してフェイルバック

スレーブの障害6

スレーブ2

マスタとスレーブの連携再開可能

p g_rew in d コマンドを使用しないでフェイルバック

スレーブの障害7

スレーブ2

マスタとスレーブの連携再開不可能

p g_b aseb acku p コマンドを使用してフェイルバック

スレーブの障害8

マスタ、ス マスタとスレーブ2 の連携再開可能
レーブ1
マスタ、スレーブ1 の障害

p g_rew in d コマンドを使用しないでフェイルバック

9

マスタ、ス マスタとスレーブ2 の連携再開不可能
レーブ1
スレーブの障害p g_b aseb acku p コマンドを使用してフェイルバック

5.4.4.1. フェ イ ルオーバ
フェイルオーバについて記載します。

5.4.4.2. 同期スレーブへのフェ イ ルオーバ

Page 65 of 285

PostgreSQL Enterprise Consortium
マスタ (server1)にて、障害が発生した場合にスレーブ1(server2)を新マスタへ昇格した構成図(1) マスタ (server1)の疑似障害発生
immediate オプションにて停止、またはp o stgres プロセスのkill する事で擬似障害を発生させます。
マスタ (server1)にて実行します。

$ pg_ctl -w -m immediate stop

$ kill -9 `head -1 $PGDATA/postmaster.pid`(2) マスタ (server1)の死活監視にて異常を検知
死活監視の方法には多数ありますが、ここではp g_isreadyコマンドを使用します。
p g_isreadyコマンドはクライアントツールですので、任意のノードから実行できます。

$ pg_isready -h server1 -U postgres -d postgres
server1:5432 - no response(3) スレーブ1(server2)を新マスタへ昇格
スレーブ1(server2)で実行します。

$ pg_ctl promote※サーバログに下記内容が記載されること"datab ase system is ready to accep t co n n ectio n s"(4) 新マスタ (server2)の死活監視にて正常を確認$ pg_isready -h server2 -U postgres -d postgres
server2:5432 - accepting connections(5) スレーブ2(server3)の死活監視を確認$ pg_isready -h server3 -U postgres -d postgres
server3:5432 - accepting connections(6) 新マスタ (server2)にてデータ更新が行えることを確認(7) スレーブ2(server3)にてデータが伝播されていることを確認
以上でファイルオーバーは完了です。

5.4.4.3. 非同期スレーブへのフェ イ ルオーバ
マスタ (server1)/スレーブ1(server2)サイトに障害が発生した場合のスレーブ2(server3)を新マスタへ昇格した構成図

Page 66 of 285

PostgreSQL Enterprise Consortium(1) マスタ (server1)の疑似障害発生
immediate オプションにて停止、またはp o stgres プロセスのkill する事で擬似障害を発生させます。
マスタ (server1)にて実行します。

$ pg_ctl -w -m immediate stop

$ kill -9 `head -1 $PGDATA/postmaster.pid`(2) スレーブ1(server2)の疑似障害発生
immediate オプションにて停止、またはp o stgres プロセスのkill する事で擬似障害を発生させます。
スレーブ1(server2)にて実行します。

$ pg_ctl -w -m immediate stop

$ kill -9 `head -1 $PGDATA/postmaster.pid`(3) マスタ (server1)、スレーブ1(server2)の死活監視にて異常を検知
死活監視の方法には多数ありますが、ここではp g_isreadyコマンドを使用します。
p g_isreadyコマンドはクライアントツールですので、任意のノードから実行できます。

$ pg_isready -h server1 -U postgres -d postgres
server1:5432 - no response$ pg_isready -h server2 -U postgres -d postgres
server2:5432 - no response(4) スレーブ2(server3)を新マスタへ昇格
スレーブ2(server3)で実行します。

$ pg_ctl promote※サーバログに下記内容が記載されること"datab ase system is ready to accep t co n n ectio n s"(5) 新マスタ (server3)の死活監視にて正常を確認$ pg_isready -h server3 -U postgres -d postgres
server3:5432 - accepting connections(6) 新マスタ (server3)にてデータ更新が行えることを確認
以上でファイルオーバーは完了です。

5.4.4.4. フェ イ ルバック
フェイルバックについて記載します。

5.4.4.5. 旧マスタ を同期モード スレーブと し て フェ イ ルバック
p g_b aseb acku p を使用したフェイルバックについて記載します。 初期構築手順とほぼ同じです。

Page 67 of 285

PostgreSQL Enterprise Consortium
マスタ障害発生によるフェイルオーバ後、旧マスタを同期モード新スレーブとしたレプリケーション構成図■ 関連パラメータ
p g_b aseb acku p に必要な設定を記載します。
表 5.28 p g_b aseb acku p に必要な設定
サーバ

設定ファイ ルパラ メ ータ

設定値

内容

マスタ

p o stgresql.co n f listen _address

0.0.0.0

全てのIP アドレス(v4)からの接続を受け付ける

マスタ

p o stgresql.co n f max_w al_sen ders2

W AL ストリームオプションを付与する場合は、2 以上を設定■p g_b aseb acku p コマンドp g_b aseb acku p コマンドの主なオプションは次の通りです。
表 5.29 p g_b aseb acku p のオプション
オプショ ン

内容-D < directo ry>

出力を書き出すディレクトリを指定。

-X < meth o d>--w al-meth o d= < meth o d>

必要なW AL ファイルをバックアップに含める。meth o d(収集方式)は以下から選択。

fetch ： W AL ファイルは最後に収集
stream ：バックアップ作成中に同時に W AL をストリームで収集運用中に p g_b aseb acku p を実行する場合には stream を指定する。
fetch （最後に収集）では、必要なW AL ファイルが削除される可能性があるため。

-S < slo t_n ame>--slo t= < slo t_n ame>

W AL ストリーミングの収集に指定したレプリケーションスロットを使用。
-X stream とセットで指定。
必要なW AL ファイルが削除されるのを防ぐ事を目的とする。
運用中に p g_b aseb acku p を実行する場合に使用を検討する。
以下に注意する。
事前にレプリケーションスロットを作成する必要がある
マスタのW AL 領域の空きが十分である事を確認する-R--w rite-reco very-co n f

最低限のreco very.co n f を作成。必要に応じて reco very.co n f を加筆修正。

-r < rate>--max-rate= < rate>

サーバから転送されるデータの最大転送速度を指定。
運用中に p g_b aseb acku p を実行する場合に使用を検討する。
転送速度を抑える事で、マスタに対する影響を制限する事を目的とする。

-P

進行状況報告を有効化。
p g_b aseb acku p 処理中におおよその進行状況を報告する。
運用中に実行した場合、データベースクラスタのサイズが増加して進行状況が100% を超える場合がある。

-v

冗長モードを有効化。
進行状況報告も有効な場合、現在処理中のファイル名を出力。

p g_b aseb acku p でレプリケーションスロットが使用できます。 W AL 収集方式に stream を指定する事でW AL をほぼ確保できますが、スロットを指定する事でより確実になります。 スロットを使用する運用であれば、この段階で作成するのが有力です。

■ フェイルバック手順
p g_b aseb acku p にスロットを指定する場合を記載します。
(1) 旧マスタ (server1)のデータベースクラスタを削除
Page 68 of 285

PostgreSQL Enterprise Consortium
旧マスタ (server1)にて実行します。

$ rm -rf $PGDATA/*※$PG DATA 以外に表領域を作成している場合、そのファイルも削除します。
※$PG DATA ディレクトリを削除する場合は、p o stgres ユーザで$PG DATA ディレクトリを作成できるよう
親ディレクトリのオーナーまたはパーミッションを設定します。
(2) レプリケーションスロットの作成
新マスタ (server2)にて実行します。

$ psql=# SELECT pg_create_physical_replication_slot('slot_server2',true);
pg_create_physical_replication_slot------------------------------------(slot_server2,0/96000090)(1 行)-- 第2パラメータに trueを指定=# \x on
拡張表示は on です。

=# SELECT * FROM pg_replication_slots;-[ RECORD 1 ]-------+------------slot_name| slot_server2
plugin|slot_type| physicaldatoid|database|
temporary| f
active| f-- まだスロットは使用されてないため false
active_pid|
xmin|
catalog_xmin|
restart_lsn| 0/96000090-- trueの指定により、作成直後から restat_lsnを認識
confirmed_flush_lsn |(3) p g_b aseb acku p コマンドにて、新マスタ (server2)からデータベースクラスタをコピー
旧マスタ (server1)にて実行します。

$ pg_basebackup -h server2 -U repuser -D $PGDATA -X stream -S slot_server2 -P -v -R
pg_basebackup: initiating base backup, waiting for checkpoint to complete
pg_basebackup: checkpoint completed
pg_basebackup: write-ahead log start point: 0/99000028 on timeline 32
pg_basebackup: starting background WAL receiver138336/138336 kB (100%), 1/1 tablespace
pg_basebackup: write-ahead log end point: 0/99000130
pg_basebackup: waiting for background process to finish streaming ...
pg_basebackup: base backup completed(4) reco very.co n f の修正
旧マスタ (server1)にて実行します。
p g_b aseb acku p にて -R オプションを指定した事で、reco very.co n f が作成されます。
スロットを指定している事から、p rimary_slo t_n ame の指定があります。以下を追記します。
p rimary_co n n in fo パラメータに ap p licatio n _n ame を追加（任意／デフォルトはw alreceiver ）reco very_target_timelin e パラメータ追加

Page 69 of 285

PostgreSQL Enterprise Consortium$ vi $PGDATA/recovery.conf[編集前]
standby_mode = 'on'
primary_conninfo = 'user=repuser passfile=''/home/p101/.pgpass'' host=server2 port=5432
sslmode=disable sslcompression=1 target_session_attrs=any'
primary_slot_name = 'slot_server2'[編集後]
standby_mode = 'on'
primary_conninfo = 'user=repuser passfile=''/home/p101/.pgpass'' host=server2 port=5432
application_name=server1 sslmode=disable sslcompression=1 target_session_attrs=any'
primary_slot_name = 'slot_server2'
recovery_target_timeline = 'latest'
起動$ pg_ctl start(5) 新マスタ (server2)のp o stgresql.co n f の修正
新マスタ (server2)にて実行します。
syn ch ro n o u s_co mmit パラメータの有効化
syn ch ro n o u s_stan db y_n ames パラメータの有効化
同期レプリケーションの設定を有効化します。

$ vi $PGDATA/postgresql.conf[編集前]
synchronous_commit = offsynchronous_standby_names = ''[編集後]
synchronous_commit = onsynchronous_standby_names = 'server1'
設定を反映$ pg_ctl reload(6) レプリケーションスロットの作成
新スレーブ1(server1)にて実行します。

Page 70 of 285

PostgreSQL Enterprise Consortium$ psql=# SELECT pg_create_physical_replication_slot('slot_server1',true);
pg_create_physical_replication_slot------------------------------------(slot_server1,0/9A000060)(1 行)-- 第2パラメータに trueを指定=# \x on
拡張表示は on です。

=# SELECT * FROM pg_replication_slots;-[ RECORD 1 ]-------+------------slot_name| slot_server1
plugin|slot_type| physicaldatoid|database|
temporary| f
active| f-- まだスロットは使用されてないため false
active_pid|
xmin|
catalog_xmin|
restart_lsn| 0/9A000060-- trueの指定により、作成直後から restat_lsnを認識
confirmed_flush_lsn |(7) スレーブ2(server3)にて reco very.co n f 修正
スレーブ2(server3)にて実行します。
p rimary_co n n in fo パラメータの｢h o st= ｣部分を修正
p rimary_co n n in fo パラメータに ap p licatio n _n ame を追加（任意／デフォルトはw alreceiver ）p rimary_slo t_n ame パラメータ追加
reco very_target_timelin e パラメータ追加$ vi $PGDATA/recovery.conf[編集前]
standby_mode = 'on'
primary_conninfo = 'user=repuser passfile=''/home/p101/.pgpass'' host=server2 port=5432
sslmode=disable sslcompression=1 target_session_attrs=any'[編集後]standby_mode = 'on'
primary_conninfo = 'user=repuser passfile=''/home/p101/.pgpass'' host=server1 port=5432
application_name=server3 sslmode=disable sslcompression=1 target_session_attrs=any'
primary_slot_name = 'slot_server1'
recovery_target_timeline = 'latest'
再起動$ pg_ctl restart(8) 新マスタ (server2)でのレプリケーション確認
p g_stat_rep licatio n s ビューを参照して、レプリケーション構成である事を確認します。
p g_rep licatio n _slo ts ビューを参照して、スロットがアクティブである事を確認します。

Page 71 of 285

PostgreSQL Enterprise Consortium$ psql=# SELECT * FROM pg_stat_replication;-[ RECORD 1 ]----+-----------------------------pid| 46558
usesysid| 16384
usename| repuser
application_name | server1
client_addr| <server1IP>
client_hostname |client_port| 64897
backend_start| 2018-02-09 11:22:23.535181+09
backend_xmin|
state| streaming--- ストリーミング中
sent_lsn| 0/9A000140
write_lsn| 0/9A000140
flush_lsn| 0/9A000140
replay_lsn| 0/9A000140
write_lag|
flush_lag|
replay_lag|
sync_priority| 1
sync_state| sync--- 同期=# SELECT slot_name,active FROM pg_replication_slots;-[ RECORD 1 ]-------+------------slot_name| slot_server2
active| t--- アクティブ(9) 新スレーブ(server1)でのレプリケーション確認
p g_stat_rep licatio n s ビューを参照して、レプリケーション構成である事を確認します。
p g_rep licatio n _slo ts ビューを参照して、スロットがアクティブである事を確認します。

$ psql=# SELECT * FROM pg_stat_replication;-[ RECORD 1 ]----+-----------------------------pid| 97277
usesysid| 16384
usename| repuser
application_name | server3
client_addr| <server3IP>
client_hostname |client_port| 52882
backend_start| 2018-02-09 11:55:20.971553+09
backend_xmin|
state| streaming--- ストリーミング中
sent_lsn| 0/9A000140
write_lsn| 0/9A000140
flush_lsn| 0/9A000140
replay_lsn| 0/9A000140
write_lag|
flush_lag|
replay_lag|
sync_priority| 0
sync_state| async--- 非同期
これにて、以下の構成に復旧しました。
レプリケーションスロット使用
server2(マスタ ) = 同期= > server1(スレーブ1) = 非同期= > server3(スレーブ2) のカスケードレプリケーション構成

5.4.4.6. 旧マスタ をイ ンスタ ンス障害から フェ イ ルバック

Page 72 of 285

PostgreSQL Enterprise Consortium

p g_b aseb acku p を使用したフェイルバックについて記載します。 初期構築手順とほぼ同じです。
マスタインスタンス障害発生によるフェイルオーバ後、旧マスタを同期モード新スレーブとしたレプリケーション構成図■ フェイルバック手順(1) スレーブ2(server3)を一旦停止
スレーブ2(server3)にて実行します。

$ pg_ctl stop(2) レプリケーションスロットの作成
新マスタ (server2)にて実行します。

$ psql=# SELECT pg_create_physical_replication_slot('slot_server2',true);
pg_create_physical_replication_slot------------------------------------(slot_server2,0/96000090)(1 行)-- 第2パラメータに trueを指定=# \x on
拡張表示は on です。

=# SELECT * FROM pg_replication_slots;-[ RECORD 1 ]-------+------------slot_name| slot_server2
plugin|slot_type| physicaldatoid|database|
temporary| f
active| f-- まだスロットは使用されてないため false
active_pid|
xmin|
catalog_xmin|
restart_lsn| 0/96000090-- trueの指定により、作成直後から restat_lsnを認識
confirmed_flush_lsn |(3) reco very.co n f の作成
旧マスタ (server1)にて実行します。
以下を設定します。

$ vi $PGDATA/recovery.conf
standby_mode = 'on'
primary_conninfo = 'user=repuser passfile=''/home/p101/.pgpass'' host=server2 port=5432
application_name=server1 sslmode=disable sslcompression=1 target_session_attrs=any'
primary_slot_name = 'slot_server2'
recovery_target_timeline = 'latest'(4) 旧マスタ (server1)のp o stgresql.co n f の修正

Page 73 of 285

PostgreSQL Enterprise Consortium
旧マスタ (server1)にて、パラメータの調整を行います。

syn ch ro n o u s_co mmit パラメータの無効化
syn ch ro n o u s_stan db y_n ames パラメータの無効化
p g_statsin fo の無効化(有効化されている場合）
同期レプリケーションの設定を無効化します。
新スレーブのsh ared_p relo ad_lib raries パラメータに p g_statsin fo が設定されている場合は、書き込みができずにエラーが発生します。
p g_statsin fo の指定を削除します。

$ vi $PGDATA/postgresql.conf[編集前]
synchronous_commit = onsynchronous_standby_names = '*'shared_preload_libraries = 'pg_stat_statements,pg_statsinfo'[編集後]
synchronous_commit = offsynchronous_standby_names = ''shared_preload_libraries = ''(5) 新スレーブ(server1)の起動
新スレーブ(server1)を起動します。

$ pg_ctl start(6) 新マスタ (server2)のp o stgresql.co n f の修正
新マスタ (server2)にて実行します。
syn ch ro n o u s_co mmit パラメータの有効化
syn ch ro n o u s_stan db y_n ames パラメータの有効化
同期レプリケーションの設定を有効化します。

$ vi $PGDATA/postgresql.conf[編集前]
synchronous_commit = offsynchronous_standby_names = ''[編集後]
synchronous_commit = onsynchronous_standby_names = 'server1'
設定を反映$ pg_ctl reload(7) レプリケーションスロットの作成
新スレーブ1(server1)にて実行します。

Page 74 of 285

PostgreSQL Enterprise Consortium$ psql=# SELECT pg_create_physical_replication_slot('slot_server1',true);
pg_create_physical_replication_slot------------------------------------(slot_server1,0/9A000060)(1 行)-- 第2パラメータに trueを指定=# \x on
拡張表示は on です。

=# SELECT * FROM pg_replication_slots;-[ RECORD 1 ]-------+------------slot_name| slot_server1
plugin|slot_type| physicaldatoid|database|
temporary| f
active| f-- まだスロットは使用されてないため false
active_pid|
xmin|
catalog_xmin|
restart_lsn| 0/9A000060-- trueの指定により、作成直後から restat_lsnを認識
confirmed_flush_lsn |(8) スレーブ2(server3)にて reco very.co n f 修正
スレーブ2(server3)にて実行します。
p rimary_co n n in fo パラメータの｢h o st= ｣部分を修正
p rimary_co n n in fo パラメータに ap p licatio n _n ame を追加（任意／デフォルトはw alreceiver ）p rimary_slo t_n ame パラメータ追加
reco very_target_timelin e パラメータ追加$ vi $PGDATA/recovery.conf[編集前]
standby_mode = 'on'
primary_conninfo = 'user=repuser passfile=''/home/p101/.pgpass'' host=server2 port=5432
sslmode=disable sslcompression=1 target_session_attrs=any'[編集後]standby_mode = 'on'
primary_conninfo = 'user=repuser passfile=''/home/p101/.pgpass'' host=server1 port=5432
application_name=server3 sslmode=disable sslcompression=1 target_session_attrs=any'
primary_slot_name = 'slot_server1'
recovery_target_timeline = 'latest'
再起動$ pg_ctl restart(9) 新マスタ (server2)でのレプリケーション確認
p g_stat_rep licatio n s ビューを参照して、レプリケーション構成である事を確認します。
p g_rep licatio n _slo ts ビューを参照して、スロットがアクティブである事を確認します。

Page 75 of 285

PostgreSQL Enterprise Consortium$ psql=# SELECT * FROM pg_stat_replication;-[ RECORD 1 ]----+-----------------------------pid| 12505
usesysid| 16384
usename| repuser
application_name | server1
client_addr| <server1IP>
client_hostname |client_port| 58195
backend_start| 2018-02-07 21:55:00.722782+09
backend_xmin|
state| streaming--- ストリーミング中
sent_lsn| 0/6A000818
write_lsn| 0/6A000818
flush_lsn| 0/6A000818
replay_lsn| 0/6A000818
write_lag|
flush_lag|
replay_lag|
sync_priority| 1
sync_state| sync--- 同期(10) 新スレーブ(server1)でのレプリケーション確認
p g_stat_rep licatio n s ビューを参照して、レプリケーション構成である事を確認します。
p g_rep licatio n _slo ts ビューを参照して、スロットがアクティブである事を確認します。

$ psql=# SELECT * FROM pg_stat_replication;-[ RECORD 1 ]----+-----------------------------pid| 86651
usesysid| 16384
usename| repuser
application_name | server3
client_addr| <server3IP>
client_hostname |client_port| 18206
backend_start| 2018-02-07 22:13:40.199786+09
backend_xmin
| 606
state
| streaming--- ストリーミング中
sent_lsn| 0/6A000818
write_lsn| 0/6A000818
flush_lsn| 0/6A000818
replay_lsn| 0/6A000818
write_lag|
flush_lag|
replay_lag|
sync_priority| 0
sync_state| async--- 非同期
これにて、以下の構成に復旧しました。
レプリケーションスロット使用
server2(マスタ ) = 同期= > server1(スレーブ1) -非同期-> server3(スレーブ2) のカスケードレプリケーション構成

5.4.4.7. 旧マスタ / 旧スレーブ 1 をフェ イ ルバック
p g_b aseb acku p を使用したフェイルバックについて記載します。 初期構築手順とほぼ同じです。
マスタ (server1)/スレーブ1(server2)サイトに障害が発生し、フェイルオーバ後、 マスタ (server1)/スレーブ1(server2)を元の状態に復旧したレプリケーション構成図

Page 76 of 285

PostgreSQL Enterprise Consortium

p g_b aseb acku p に必要な設定については、以下と同様であるため省略します。
｢旧マスタを同期モードスレーブとしてフェイルバック｣

p g_b aseb acku p でレプリケーションスロットが使用できます。 W AL 収集方式に stream を指定する事でW AL をほぼ確保できますが、スロットを指定する事でより確実になります。 スロットを使用する運用であれば、この段階で作成するのが有力です。

■ フェイルバック手順
p g_b aseb acku p にスロットを指定する場合を記載します。
(1) 旧マスタ (server1)のデータベースクラスタを削除
旧マスタ (server1)にて実行します。

$ rm -rf $PGDATA/*※$PG DATA 以外に表領域を作成している場合、そのファイルも削除します。
※$PG DATA ディレクトリを削除する場合は、p o stgres ユーザで$PG DATA ディレクトリを作成できるよう
親ディレクトリのオーナーまたはパーミッションを設定します。
(2) 旧スレーブ1(server2)のデータベースクラスタを削除
旧スレーブ1(server2)にて実行します。

$ rm -rf $PGDATA/*※$PG DATA 以外に表領域を作成している場合、そのファイルも削除します。
※$PG DATA ディレクトリを削除する場合は、p o stgres ユーザで$PG DATA ディレクトリを作成できるよう
親ディレクトリのオーナーまたはパーミッションを設定します。
(3) レプリケーションスロットの作成
新マスタ (server3)にて実行します。

$ psql=# SELECT pg_create_physical_replication_slot('slot_server3',true);
pg_create_physical_replication_slot------------------------------------(slot_server3,0/AD000090)(1 行)-- 第2パラメータに trueを指定=# \x on
拡張表示は on です。

=# SELECT * FROM pg_replication_slots;-[ RECORD 1 ]-------+------------slot_name| slot_server3
plugin|slot_type| physicaldatoid|database|
temporary| f
active| f-- まだスロットは使用されてないため false
active_pid|
xmin|
catalog_xmin|
restart_lsn| 0/AD000090-- trueの指定により、作成直後から restat_lsnを認識
confirmed_flush_lsn |Page 77 of 285

PostgreSQL Enterprise Consortium(4) p g_b aseb acku p コマンドにて、新マスタ (server3)からデータベースクラスタをコピー
旧マスタ (server1)にて実行します。

$ pg_basebackup -h server3 -U repuser -D $PGDATA -X stream -S slot_server3 -P -v -R
pg_basebackup: initiating base backup, waiting for checkpoint to complete
pg_basebackup: checkpoint completed
pg_basebackup: write-ahead log start point: 0/3B000028 on timeline 2
pg_basebackup: starting background WAL receiver
203852/203852 kB (100%), 1/1 tablespace
pg_basebackup: write-ahead log end point: 0/3B000130
pg_basebackup: waiting for background process to finish streaming ...
pg_basebackup: base backup completed(5) reco very.co n f の修正
旧マスタ (server1)にて実行します。
p g_b aseb acku p にて -R オプションを指定した事で、reco very.co n f が作成されます。
スロットを指定している事から、p rimary_slo t_n ame の指定があります。以下を追記します。
p rimary_co n n in fo パラメータに ap p licatio n _n ame を追加（任意／デフォルトはw alreceiver ）p rimary_slo t_n ame パラメータ追加
reco very_target_timelin e パラメータ追加$ vi $PGDATA/recovery.conf[編集前]
standby_mode = 'on'
primary_conninfo = 'user=repuser passfile=''/home/p101/.pgpass'' host=server3 port=5432
sslmode=disable sslcompression=1 target_session_attrs=any'
primary_slot_name = 'slot_server3'[編集後]
standby_mode = 'on'
primary_conninfo = 'user=repuser passfile=''/home/p101/.pgpass'' host=server3 port=5432
application_name=server1 sslmode=disable sslcompression=1 target_session_attrs=any'
primary_slot_name = 'slot_server3'
recovery_target_timeline = 'latest'(6) 新スレーブ1(server1)の起動
新スレーブ1(server1)を起動します。

$ pg_ctl start(7) レプリケーションスロットの作成
新スレーブ1(server1)にて実行します。

Page 78 of 285

PostgreSQL Enterprise Consortium$ psql=# SELECT pg_create_physical_replication_slot('slot_server1',true);
pg_create_physical_replication_slot------------------------------------(slot_server1,0/AE000028)(1 行)-- 第2パラメータに trueを指定=# \x on
拡張表示は on です。

=# SELECT * FROM pg_replication_slots;-[ RECORD 1 ]-------+------------slot_name| slot_server1
plugin|slot_type| physicaldatoid|database|
temporary| f
active| f-- まだスロットは使用されてないため false
active_pid|
xmin|
catalog_xmin|
restart_lsn| 0/AE000028-- trueの指定により、作成直後から restat_lsnを認識
confirmed_flush_lsn |(8) p g_b aseb acku p コマンドにて、新スレーブ1(server1)からデータベースクラスタをコピー
スレーブ2(server2)にて実行します。

$ pg_basebackup -h server1 -U repuser -D $PGDATA -X stream -S slot_server1 -P -v -R
pg_basebackup: initiating base backup, waiting for checkpoint to complete
pg_basebackup: checkpoint completed
pg_basebackup: write-ahead log start point: 0/3B000028 on timeline 2
pg_basebackup: starting background WAL receiver
203852/203852 kB (100%), 1/1 tablespace
pg_basebackup: write-ahead log end point: 0/3B000130
pg_basebackup: waiting for background process to finish streaming ...
pg_basebackup: base backup completed(9) reco very.co n f の修正
スレーブ2(server2)にて実行します。
p g_b aseb acku p にて -R オプションを指定した事で、reco very.co n f が作成されます。
スロットを指定している事から、p rimary_slo t_n ame の指定があります。以下を追記します。
p rimary_co n n in fo パラメータに ap p licatio n _n ame を追加（任意／デフォルトはw alreceiver ）p rimary_slo t_n ame パラメータ追加
reco very_target_timelin e パラメータ追加$ vi $PGDATA/recovery.conf[編集前]
standby_mode = 'on'
primary_conninfo = 'user=repuser passfile=''/home/p101/.pgpass'' host=server1 port=5432
sslmode=disable sslcompression=1 target_session_attrs=any'
primary_slot_name = 'slot_server1'[編集後]
standby_mode = 'on'
primary_conninfo = 'user=repuser passfile=''/home/p101/.pgpass'' host=server1 port=5432
application_name=server2 sslmode=disable sslcompression=1 target_session_attrs=any'
primary_slot_name = 'slot_server1'
recovery_target_timeline = 'latest'(10) 新スレーブ2(server2)の起動

Page 79 of 285

PostgreSQL Enterprise Consortium
新スレーブ2(server2)を起動します。

$ pg_ctl start(11) マスタ (server3)の停止
マスタ (server3)を停止します。

$ pg_ctl stop(12) スレーブ1(server1)を新マスタへ昇格
スレーブ1(server1)で実行します。

$ pg_ctl promote※サーバログに下記内容が記載されること"datab ase system is ready to accep t co n n ectio n s"(13) レプリケーションスロットの作成
新スレーブ1(server2)にて実行します。

$ psql=# SELECT pg_create_physical_replication_slot('slot_server2',true);
pg_create_physical_replication_slot------------------------------------(slot_server2,0/AE000028)(1 行)-- 第2パラメータに trueを指定=# \x on
拡張表示は on です。

=# SELECT * FROM pg_replication_slots;-[ RECORD 1 ]-------+------------slot_name| slot_server2
plugin|slot_type| physicaldatoid|database|
temporary| f
active| f-- まだスロットは使用されてないため false
active_pid|
xmin|
catalog_xmin|
restart_lsn| 0/AE000028-- trueの指定により、作成直後から restat_lsnを認識
confirmed_flush_lsn |(14) 新スレーブ2(server3)にて reco very.co n f 作成
新スレーブ2(server3)にて reco very.co n f を作成します。
p rimary_co n n in fo パラメータに ap p licatio n _n ame を追加（任意／デフォルトはw alreceiver ）p rimary_slo t_n ame パラメータの追加
reco very_target_timelin e パラメータの追加

Page 80 of 285

PostgreSQL Enterprise Consortium$ mv $PGDATA/recovery.done $PGDATA/recovery.conf$ vi $PGDATA/recovery.conf[編集前]
standby_mode = 'on'
primary_conninfo = 'user=repuser passfile=''/home/p101/.pgpass'' host=server2 port=5432
sslmode=disable sslcompression=1 target_session_attrs=any'[編集後]standby_mode = 'on'
primary_conninfo = 'user=repuser passfile=''/home/p101/.pgpass'' host=server2 port=5432
application_name=server3 sslmode=disable sslcompression=1 target_session_attrs=any'
primary_slot_name = 'slot_server2'
recovery_target_timeline = 'latest'
起動$ pg_ctl start(15) 新マスタ (server1)のp o stgresql.co n f の修正
新マスタ (server1)にて実行します。
syn ch ro n o u s_co mmit パラメータの有効化
syn ch ro n o u s_stan db y_n ames パラメータの有効化
同期レプリケーションの設定を有効化します。

$ vi $PGDATA/postgresql.conf[編集前]
synchronous_commit = offsynchronous_standby_names = ''[編集後]
synchronous_commit = onsynchronous_standby_names = 'server2'
設定を反映$ pg_ctl reload(16) 新マスタ (server1)でのレプリケーション確認
p g_stat_rep licatio n s ビューを参照して、レプリケーション構成である事を確認します。
p g_rep licatio n _slo ts ビューを参照して、スロットがアクティブである事を確認します。

Page 81 of 285

PostgreSQL Enterprise Consortium$ psql=# SELECT * FROM pg_stat_replication;-[ RECORD 1 ]----+-----------------------------pid| 12505
usesysid| 16384
usename| repuser
application_name | server2
client_addr| <server2IP>
client_hostname |client_port| 58195
backend_start| 2018-02-07 21:55:00.722782+09
backend_xmin|
state| streaming--- ストリーミング中
sent_lsn| 0/6A000818
write_lsn| 0/6A000818
flush_lsn| 0/6A000818
replay_lsn| 0/6A000818
write_lag|
flush_lag|
replay_lag|
sync_priority| 1
sync_state| sync--- 同期=# SELECT slot_name,active FROM pg_replication_slots;-[ RECORD 1 ]-------+------------slot_name| slot_server1
active| t--- アクティブ(17) 新スレーブ1(server2)でのレプリケーション確認
p g_stat_rep licatio n s ビューを参照して、レプリケーション構成である事を確認します。
p g_rep licatio n _slo ts ビューを参照して、スロットがアクティブである事を確認します。

$ psql=# SELECT * FROM pg_stat_replication;-[ RECORD 1 ]----+-----------------------------pid| 86651
usesysid| 16384
usename| repuser
application_name | server3
client_addr| <server3IP>
client_hostname |client_port| 18206
backend_start| 2018-02-07 22:13:40.199786+09
backend_xmin
| 606
state
| streaming--- ストリーミング中
sent_lsn| 0/6A000818
write_lsn| 0/6A000818
flush_lsn| 0/6A000818
replay_lsn| 0/6A000818
write_lag|
flush_lag|
replay_lag|
sync_priority| 0
sync_state| async--- 非同期=# SELECT slot_name,active FROM pg_replication_slots;
slot_name | active--------------+-------slot_server2 | t(1 row)(18) レプリケーションスロットの削除(スレーブ）

Page 82 of 285

PostgreSQL Enterprise Consortium
新スレーブに旧マスタ時代のスロットが残る事があります。
restart_lsn 列に値が残っている状態では、マスタのVACU U M 処理を阻害するなどの悪影響の可能性があるため、削除します。
スロットの削除は関数で行うため、スレーブでも実行可能です。
スレーブ2(server3)にて実行します。

$ psql=# SELECT slot_name,active FROM pg_replication_slots;
slot_name | active--------------+-------slot_server3 | f(1 row)=# SELECT pg_drop_replication_slot('slot_server3');
pg_create_physical_replication_slot------------------------------------(1 row)=# SELECT slot_name FROM pg_replication_slots;(0 rows)

これにて、以下の構成に復旧しました。
レプリケーションスロット使用
server1(マスタ ) = 同期= > server2(スレーブ1) = 非同期= > server3(スレーブ2) のカスケードレプリケーション構成

5.4.4.8. スイ ッチ オーバ
スイッチオーバについて記載します。

5.4.4.9. 同期スレーブへのスイ ッチ オーバ■ スイッチオーバ手順
計画停止におけるマスタ /スレーブの切り替え手順です。 p g_b aseb acku p やp g_rew in d が不要であるためシンプルな手順です。
計画停止にてマスタとスレーブ1 の役割を切り替えたレプリケーション構成図(1) マスタ (server1)の正常停止
マスタ (server1)にて実行します。

$ pg_ctl stop -m fast(2) スレーブ(server2)の昇格
スレーブ(server2)にて実行します。
以下の場合にはp g_rew in d 不要です。
計画停止
同期レプリケーションのインスタンス障害(物理障害なし）

Page 83 of 285

PostgreSQL Enterprise Consortium$ pg_ctl promote(3) レプリケーションスロットの作成
新マスタ (server2)にて実行します。

$ psql=# SELECT pg_create_physical_replication_slot('slot_server2',true);
pg_create_physical_replication_slot------------------------------------(slot_server2,0/B40000C8)(1 行)-- 第2パラメータに trueを指定=# \x on
拡張表示は on です。

=# SELECT * FROM pg_replication_slots;-[ RECORD 1 ]-------+------------slot_name| slot_server2
plugin|slot_type| physicaldatoid|database|
temporary| f
active| f-- まだスロットは使用されてないため false
active_pid|
xmin|
catalog_xmin|
restart_lsn| 0/B40000C8-- trueの指定により、作成直後から restat_lsnを認識
confirmed_flush_lsn |(4) 旧マスタ (server1)のreco very.co n f の作成
以下を記載します。

$ vi $PGDATA/recovery.conf
standby_mode = 'on'
primary_conninfo = 'user=repuser passfile=''/home/p101/.pgpass'' host=server2 port=5432
application_name=server1 sslmode=disable sslcompression=1 target_session_attrs=any'
primary_slot_name = 'slot_server2'
recovery_target_timeline = 'latest'(5) 旧マスタ (server1)のp o stgresql.co n f の修正
旧マスタ (server1)にて、パラメータの調整を行います。
syn ch ro n o u s_co mmit パラメータの無効化
syn ch ro n o u s_stan db y_n ames パラメータの無効化
p g_statsin fo の無効化(有効化されている場合）
同期レプリケーションの設定を無効化します。
新スレーブのsh ared_p relo ad_lib raries パラメータに p g_statsin fo が設定されている場合は、書き込みができずにエラーが発生します。
p g_statsin fo の指定を削除します。

Page 84 of 285

PostgreSQL Enterprise Consortium$ vi $PGDATA/postgresql.conf[編集前]
synchronous_commit = onsynchronous_standby_names = '*'shared_preload_libraries = 'pg_stat_statements,pg_statsinfo'[編集後]
synchronous_commit = offsynchronous_standby_names = ''shared_preload_libraries = ''再起動

$ pg_ctl restart(6) レプリケーションスロットの作成
新スレーブ(server1)にて実行します。

$ psql=# SELECT pg_create_physical_replication_slot('slot_server1',true);
pg_create_physical_replication_slot------------------------------------(slot_server1,0/B4000028)(1 行)-- 第2パラメータに trueを指定=# \x on
拡張表示は on です。

=# SELECT * FROM pg_replication_slots;-[ RECORD 1 ]-------+------------slot_name| slot_server1
plugin|slot_type| physicaldatoid|database|
temporary| f
active| f-- まだスロットは使用されてないため false
active_pid|
xmin|
catalog_xmin|
restart_lsn| 0/B4000028-- trueの指定により、作成直後から restat_lsnを認識
confirmed_flush_lsn |(7) 新マスタ (server2)のp o stgresql.co n f の修正
新マスタ (server2)にて実行します。
syn ch ro n o u s_co mmit パラメータの有効化
syn ch ro n o u s_stan db y_n ames パラメータの有効化
同期レプリケーションの設定を有効化します。

$ vi $PGDATA/postgresql.conf[編集前]
synchronous_commit = offsynchronous_standby_names = ''[編集後]
synchronous_commit = onsynchronous_standby_names = 'server1'
設定を反映$ pg_ctl reload

Page 85 of 285

PostgreSQL Enterprise Consortium(8) スレーブ2(server3)にて reco very.co n f 修正
スレーブ2(server3)にて実行します。
p rimary_co n n in fo パラメータの｢h o st= ｣部分を修正
p rimary_co n n in fo パラメータに ap p licatio n _n ame を追加（任意／デフォルトはw alreceiver ）p rimary_slo t_n ame パラメータ追加
reco very_target_timelin e パラメータ追加$ vi $PGDATA/recovery.conf[編集前]
primary_conninfo = 'user=repuser passfile=''/home/p101/.pgpass'' host=server2 port=12079
sslmode=disable sslcompression=1 target_session_attrs=any'[編集後]
primary_conninfo = 'user=repuser passfile=''/home/p101/.pgpass'' host=server1 port=12079
application_name=server3 sslmode=disable sslcompression=1 target_session_attrs=any'
primary_slot_name = 'slot_server1'
recovery_target_timeline = 'latest'
再起動$ pg_ctl restart(9) 新マスタ (server2)でのレプリケーション確認
p g_stat_rep licatio n s ビューを参照して、レプリケーション構成である事を確認します。
p g_rep licatio n _slo ts ビューを参照して、スロットがアクティブである事を確認します。

$ psql=# SELECT * FROM pg_stat_replication;-[ RECORD 1 ]----+-----------------------------pid| 12505
usesysid| 16384
usename| repuser
application_name | server1
client_addr| <server1IP>
client_hostname |client_port| 58195
backend_start| 2018-02-07 21:55:00.722782+09
backend_xmin|
state| streaming--- ストリーミング中
sent_lsn| 0/6A000818
write_lsn| 0/6A000818
flush_lsn| 0/6A000818
replay_lsn| 0/6A000818
write_lag|
flush_lag|
replay_lag|
sync_priority| 1
sync_state| sync--- 同期=# SELECT slot_name,active FROM pg_replication_slots;-[ RECORD 1 ]-------+------------slot_name| slot_server2
active| t--- アクティブ(10) 新スレーブ(server1)でのレプリケーション確認
p g_stat_rep licatio n s ビューを参照して、レプリケーション構成である事を確認します。
p g_rep licatio n _slo ts ビューを参照して、スロットがアクティブである事を確認します。

Page 86 of 285

PostgreSQL Enterprise Consortium$ psql=# SELECT * FROM pg_stat_replication;-[ RECORD 1 ]----+-----------------------------pid| 86651
usesysid| 16384
usename| repuser
application_name | server3
client_addr| <server3IP>
client_hostname |client_port| 18206
backend_start| 2018-02-07 22:13:40.199786+09
backend_xmin
| 606
state
| streaming--- ストリーミング中
sent_lsn| 0/6A000818
write_lsn| 0/6A000818
flush_lsn| 0/6A000818
replay_lsn| 0/6A000818
write_lag|
flush_lag|
replay_lag|
sync_priority| 0
sync_state| async--- 非同期=# SELECT slot_name,active FROM pg_replication_slots;-[ RECORD 1 ]----------slot_name | slot_server1
active| f
以上でスイッチオーバが完了しました。

5.4.4.10. 非同期スレーブへのスイ ッチ オーバ■ スイッチオーバ手順
計画停止におけるマスタ /スレーブの切り替え手順です。 p g_b aseb acku p やp g_rew in d が不要であるためシンプルな手順です。
計画停止にてマスタとスレーブ2 の役割を切り替えたレプリケーション構成図(1) マスタ (server1)の正常停止
マスタ (server1)にて実行します。

$ pg_ctl stop -m fast(2) スレーブ(server3)の昇格
スレーブ(server3)にて実行します。
以下の場合にはp g_rew in d 不要です。
計画停止
レプリケーションのインスタンス障害(物理障害なし）

Page 87 of 285

PostgreSQL Enterprise Consortium$ pg_ctl promote(3) レプリケーションスロットの作成
新マスタ (server3)にて実行します。

$ psql=# SELECT pg_create_physical_replication_slot('slot_server3',true);
pg_create_physical_replication_slot------------------------------------(slot_server3,0/BA0000C8)(1 行)-- 第2パラメータに trueを指定=# \x on
拡張表示は on です。

=# SELECT * FROM pg_replication_slots;-[ RECORD 1 ]-------+------------slot_name| slot_server3
plugin|slot_type| physicaldatoid|database|
temporary| f
active| f-- まだスロットは使用されてないため false
active_pid|
xmin|
catalog_xmin|
restart_lsn| 0/BA0000C8-- trueの指定により、作成直後から restat_lsnを認識
confirmed_flush_lsn |(4) 旧マスタ (server1)のreco very.co n f の作成
以下を記載します。

$ vi $PGDATA/recovery.conf
standby_mode = 'on'
primary_conninfo = 'user=repuser passfile=''/home/p101/.pgpass'' host=server3 port=5432
application_name=server1 sslmode=disable sslcompression=1 target_session_attrs=any'
primary_slot_name = 'slot_server3'
recovery_target_timeline = 'latest'(5) 旧マスタ (server1)のp o stgresql.co n f の修正
旧マスタ (server1)にて、パラメータの調整を行います。
syn ch ro n o u s_co mmit パラメータの無効化
syn ch ro n o u s_stan db y_n ames パラメータの無効化
p g_statsin fo の無効化(有効化されている場合）
同期レプリケーションの設定を無効化します。
新スレーブのsh ared_p relo ad_lib raries パラメータに p g_statsin fo が設定されている場合は、書き込みができずにエラーが発生します。
p g_statsin fo の指定を削除します。

Page 88 of 285

PostgreSQL Enterprise Consortium$ vi $PGDATA/postgresql.conf[編集前]
synchronous_commit = onsynchronous_standby_names = '*'shared_preload_libraries = 'pg_stat_statements,pg_statsinfo'[編集後]
synchronous_commit = offsynchronous_standby_names = ''shared_preload_libraries = ''(6) 新スレーブ1(server1)の起動
新スレーブ1(server1)を起動します。

$ pg_ctl start(7) レプリケーションスロットの作成
新スレーブ1(server1)にて実行します。

$ psql=# SELECT pg_create_physical_replication_slot('slot_server1',true);
pg_create_physical_replication_slot------------------------------------(slot_server1,0/BA000028)(1 行)-- 第2パラメータに trueを指定=# \x on
拡張表示は on です。

=# SELECT * FROM pg_replication_slots;-[ RECORD 1 ]-------+------------slot_name| slot_server1
plugin|slot_type| physicaldatoid|database|
temporary| f
active| f-- まだスロットは使用されてないため false
active_pid|
xmin|
catalog_xmin|
restart_lsn| 0/BA000028-- trueの指定により、作成直後から restat_lsnを認識
confirmed_flush_lsn |(8) スレーブ2(server2)にて reco very.co n f 修正
スレーブ2(server2)にて実行します。
p rimary_co n n in fo パラメータに ap p licatio n _n ame を追加（任意／デフォルトはw alreceiver ）p rimary_slo t_n ame パラメータ追加
reco very_target_timelin e パラメータ

Page 89 of 285

PostgreSQL Enterprise Consortium$ vi $PGDATA/recovery.conf[編集前]
standby_mode = 'on'
primary_conninfo = 'user=repuser passfile=''/home/p101/.pgpass'' host=server1 port=5432
sslmode=disable sslcompression=1 target_session_attrs=any'[編集後]standby_mode = 'on'
primary_conninfo = 'user=repuser passfile=''/home/p101/.pgpass'' host=server1 port=5432
application_name=server1 sslmode=disable sslcompression=1 target_session_attrs=any'
primary_slot_name = 'slot_server1'
recovery_target_timeline = 'latest'
再起動$ pg_ctl restart(9) 新マスタ (server3)でのレプリケーション確認
p g_stat_rep licatio n s ビューを参照して、レプリケーション構成である事を確認します。
p g_rep licatio n _slo ts ビューを参照して、スロットがアクティブである事を確認します。

$ psql=# SELECT * FROM pg_stat_replication;-[ RECORD 1 ]----+-----------------------------pid| 12505
usesysid| 16384
usename| repuser
application_name | server1
client_addr| <server1IP>
client_hostname |client_port| 58195
backend_start| 2018-02-07 21:55:00.722782+09
backend_xmin|
state| streaming--- ストリーミング中
sent_lsn| 0/6A000818
write_lsn| 0/6A000818
flush_lsn| 0/6A000818
replay_lsn| 0/6A000818
write_lag|
flush_lag|
replay_lag|
sync_priority| 1
sync_state| async--- 非同期=# SELECT slot_name,active FROM pg_replication_slots;-[ RECORD 1 ]-------+------------slot_name| slot_server3
active| t--- アクティブ(10) 新スレーブ1(server1)でのレプリケーション確認
p g_stat_rep licatio n s ビューを参照して、レプリケーション構成である事を確認します。
p g_rep licatio n _slo ts ビューを参照して、スロットがアクティブである事を確認します。

Page 90 of 285

PostgreSQL Enterprise Consortium$ psql=# SELECT * FROM pg_stat_replication;-[ RECORD 1 ]----+-----------------------------pid| 86651
usesysid| 16384
usename| repuser
application_name | server2
client_addr| <server2IP>
client_hostname |client_port| 18206
backend_start| 2018-02-07 22:13:40.199786+09
backend_xmin
| 606
state
| streaming--- ストリーミング中
sent_lsn| 0/6A000818
write_lsn| 0/6A000818
flush_lsn| 0/6A000818
replay_lsn| 0/6A000818
write_lag|
flush_lag|
replay_lag|
sync_priority| 0
sync_state| async--- 非同期=# SELECT slot_name,active FROM pg_replication_slots;-[ RECORD 1 ]-------+------------slot_name| slot_server1
active| t--- アクティブ
以上でスイッチオーバが完了しました。

5.4.4.11. スイ ッチ バック
スイッチバックについて記載します。

5.4.4.12. 同期モード スレーブから マスタ へのスイ ッチ バック
同期モードスレーブになっていた旧マスタをマスタに復帰させるスイッチバックついて記載します。
同期モードスレーブになっていた旧マスタをマスタとして復帰させたレプリケーション構成図■ スイッチバック手順(1) マスタ (server2)の停止$ pg_ctl stop(2) スレーブ1(server1)の昇格
スレーブ1(server1)にて実行します。

Page 91 of 285

PostgreSQL Enterprise Consortium$ pg_ctl promote(3) 旧マスタ (server2)のreco very.co n f の作成
以下を記載します。

p rimary_co n n in fo パラメータに ap p licatio n _n ame を追加（任意／デフォルトはw alreceiver ）p rimary_slo t_n ame パラメータ追加
reco very_target_timelin e パラメータ追加$ cp $PGDATA/recovery.done $PGDATA/recovery.conf$ vi $PGDATA/recovery.conf
standby_mode = 'on'
primary_conninfo = 'user=repuser passfile=''/home/p101/.pgpass'' host=server1 port=5432
application_name=server2 sslmode=disable sslcompression=1 target_session_attrs=any'
primary_slot_name = 'slot_server1'
recovery_target_timeline = 'latest'(4) 新マスタ (server1)のp o stgresql.co n f の修正
新マスタ (server1)にて実行します。
syn ch ro n o u s_co mmit パラメータの有効化
syn ch ro n o u s_stan db y_n ames パラメータの有効化
同期レプリケーションの設定を有効化します。

$ vi $PGDATA/postgresql.conf[編集前]
synchronous_commit = offsynchronous_standby_names = ''[編集後]
synchronous_commit = onsynchronous_standby_names = 'server2'
設定を反映$ pg_ctl reload(5) 新スレーブ1(server2)の起動$ pg_ctl start(6) スレーブ2(server3)のreco very.co n f の作成
以下を記載します。

p rimary_co n n in fo パラメータの｢h o st= ｣部分を修正
p rimary_co n n in fo パラメータに ap p licatio n _n ame を追加（任意／デフォルトはw alreceiver ）p rimary_slo t_n ame パラメータ修正
reco very_target_timelin e パラメータ追加

Page 92 of 285

PostgreSQL Enterprise Consortium$ vi $PGDATA/recovery.conf[編集前]
standby_mode = 'on'
primary_conninfo = 'user=repuser passfile=''/home/p101/.pgpass'' host=server1 port=5432
sslmode=disable sslcompression=1 target_session_attrs=any'
primary_slot_name = 'slot_server1'[編集後]
standby_mode = 'on'
primary_conninfo = 'user=repuser passfile=''/home/p101/.pgpass'' host=server2 port=5432
application_name=server3 sslmode=disable sslcompression=1 target_session_attrs=any'
primary_slot_name = 'slot_server2'
recovery_target_timeline = 'latest'(7) スレーブ2(server3)の再起動$ pg_ctl restart(8) 新マスタ (server1)でのレプリケーション確認
p g_stat_rep licatio n s ビューを参照して、レプリケーション構成である事を確認します。
p g_rep licatio n _slo ts ビューを参照して、スロットがアクティブである事を確認します。

$ psql=# SELECT * FROM pg_stat_replication;-[ RECORD 1 ]----+-----------------------------pid| 12505
usesysid| 16384
usename| repuser
application_name | server2
client_addr| <server2IP>
client_hostname |client_port| 58195
backend_start| 2018-02-07 21:55:00.722782+09
backend_xmin|
state| streaming--- ストリーミング中
sent_lsn| 0/6A000818
write_lsn| 0/6A000818
flush_lsn| 0/6A000818
replay_lsn| 0/6A000818
write_lag|
flush_lag|
replay_lag|
sync_priority| 1
sync_state| sync--- 同期(9) スレーブ1(server2)でのレプリケーション確認
p g_stat_rep licatio n s ビューを参照して、レプリケーション構成である事を確認します。
p g_rep licatio n _slo ts ビューを参照して、スロットがアクティブである事を確認します。

Page 93 of 285

PostgreSQL Enterprise Consortium$ psql=# SELECT * FROM pg_stat_replication;-[ RECORD 1 ]----+-----------------------------pid| 86651
usesysid| 16384
usename| repuser
application_name | server3
client_addr| <serrver3IP>
client_hostname |client_port| 18206
backend_start| 2018-02-07 22:13:40.199786+09
backend_xmin
| 606
state
| streaming--- ストリーミング中
sent_lsn| 0/6A000818
write_lsn| 0/6A000818
flush_lsn| 0/6A000818
replay_lsn| 0/6A000818
write_lag|
flush_lag|
replay_lag|
sync_priority| 0
sync_state| async--- 非同期
以上でスイッチバックが完了しました。

5.4.4.13. 非同期モード スレーブから マスタ へのスイ ッチ バック
非同期モードスレーブからマスタへスイッチバックついて記載します。
非同期モードスレーブになっていた旧マスタをマスタとして復帰させたレプリケーション構成図■ スイッチバック手順(1) マスタ (server3)の停止$ pg_ctl stop(2) スレーブ1(server1)の昇格
スレーブ1(server1)にて実行します。

$ pg_ctl promote(3) レプリケーションスロットの作成
新スレーブ1(server2)にて実行します。

Page 94 of 285

PostgreSQL Enterprise Consortium$ psql=# SELECT pg_create_physical_replication_slot('slot_server2',true);
pg_create_physical_replication_slot------------------------------------(slot_server2,0/BB0000C8)(1 行)-- 第2パラメータに trueを指定=# \x on
拡張表示は on です。

=# SELECT * FROM pg_replication_slots;-[ RECORD 1 ]-------+------------slot_name| slot_server2
plugin|slot_type| physicaldatoid|database|
temporary| f
active| f-- まだスロットは使用されてないため false
active_pid|
xmin|
catalog_xmin|
restart_lsn| 0/BB0000C8-- trueの指定により、作成直後から restat_lsnを認識
confirmed_flush_lsn |(4) 旧マスタ (server3)のreco very.co n f の作成
以下を記載します。

p rimary_co n n in fo パラメータに ap p licatio n _n ame を追加（任意／デフォルトはw alreceiver ）p rimary_slo t_n ame パラメータ追加
reco very_target_timelin e パラメータ追加$ cp $PGDATA/recovery.done $PGDATA/recovery.conf$ vi $PGDATA/recovery.conf
standby_mode = 'on'
primary_conninfo = 'user=repuser passfile=''/home/p101/.pgpass'' host=server2 port=5432
application_name=server3 sslmode=disable sslcompression=1 target_session_attrs=any'
primary_slot_name = 'slot_server2'
recovery_target_timeline = 'latest'
再起動$ pg_ctl restart(5) 新マスタ (server1)のp o stgresql.co n f の修正
新マスタ (server1)にて実行します。
syn ch ro n o u s_co mmit パラメータの有効化
syn ch ro n o u s_stan db y_n ames パラメータの有効化
同期レプリケーションの設定を有効化します。

$ vi $PGDATA/postgresql.conf[編集前]
synchronous_commit = offsynchronous_standby_names = ''[編集後]
synchronous_commit = onsynchronous_standby_names = 'server2'$ pg_ctl reload

Page 95 of 285

PostgreSQL Enterprise Consortium(6) 新マスタ (server1)でのレプリケーション確認
p g_stat_rep licatio n s ビューを参照して、レプリケーション構成である事を確認します。
p g_rep licatio n _slo ts ビューを参照して、スロットがアクティブである事を確認します。

$ psql=# SELECT * FROM pg_stat_replication;-[ RECORD 1 ]----+-----------------------------pid| 12505
usesysid| 16384
usename| repuser
application_name | server2
client_addr| <server2IP>
client_hostname |client_port| 58195
backend_start| 2018-02-07 21:55:00.722782+09
backend_xmin|
state| streaming--- ストリーミング中
sent_lsn| 0/6A000818
write_lsn| 0/6A000818
flush_lsn| 0/6A000818
replay_lsn| 0/6A000818
write_lag|
flush_lag|
replay_lag|
sync_priority| 1
sync_state| sync--- 同期=# SELECT slot_name,active FROM pg_replication_slots;-[ RECORD 1 ]-------+------------slot_name| slot_server1
active| t--- アクティブ(7) スレーブ1(server2)でのレプリケーション確認
p g_stat_rep licatio n s ビューを参照して、レプリケーション構成である事を確認します。
p g_rep licatio n _slo ts ビューを参照して、スロットがアクティブである事を確認します。

Page 96 of 285

PostgreSQL Enterprise Consortium$ psql=# SELECT * FROM pg_stat_replication;-[ RECORD 1 ]----+-----------------------------pid| 86651
usesysid| 16384
usename| repuser
application_name | server3
client_addr| <server3IP>
client_hostname |client_port| 18206
backend_start| 2018-02-07 22:13:40.199786+09
backend_xmin
| 606
state
| streaming--- ストリーミング中
sent_lsn| 0/6A000818
write_lsn| 0/6A000818
flush_lsn| 0/6A000818
replay_lsn| 0/6A000818
write_lag|
flush_lag|
replay_lag|
sync_priority| 0
sync_state| async--- 非同期=# SELECT slot_name,active FROM pg_replication_slots;-[ RECORD 1 ]-------+------------slot_name| slot_server2
active| t--- アクティブ(8) レプリケーションスロットの削除(スレーブ）
フェイルオーバとの違いとしては、新スレーブに旧マスタ時代のスロットが残る事があります。
restart_lsn 列に値が残っている状態では、マスタのVACU U M 処理を阻害するなどの悪影響の可能性があるため、削除します。
スロットの削除は関数で行うため、スレーブでも実行可能です。
スレーブ2(server3)にて実施します。

$ psql=# SELECT slot_name FROM pg_replication_slots;
slot_name-------------slot_server3(1 row)=# SELECT pg_drop_replication_slot('slot_server3');
pg_create_physical_replication_slot------------------------------------(1 row)=# SELECT slot_name FROM pg_replication_slots;(0 rows)

以上でスイッチバックが完了しました。

5.4.4.14. pg_rewind を使用し た スイ ッチ バック
p g_rew in d を使用したスイッチバックについて記載します。
p g_rew in d はタイムラインのずれたレプリケーションを再同期させる機能です。 実行後、ターゲットクラスタはソースクラスタと置き換えられた状態になります。 そのため
p g_rew in d 後の操作は、通常のスイッチオーバ時と同じです。 タイムラインの分岐点からソースクラスタのW AL を適用するため、更新量が少なければp g_b aseb ack による複製より高速です。 これによりスイッチオーバ時、旧マスタを容易に新スレーブとして起動させることができます。
スイッチオーバー後、p g_rew in d で旧マスタを新スレーブとして戻したレプリケーション構成図

Page 97 of 285

PostgreSQL Enterprise Consortium■ 関連パラメータ
p g_rew in d に必要な設定を記載します。
表 5.30 p g_rew in d に必要な設定
サーバ

設定ファイ ル

p g_rew in d p o stgresql.co n f

パラ メ ータ

設定値

fu ll_p age_w riteson

チェックポイント後の更新時、ディスクページの全内容をW AL に書き込む。

w al_lo g_h in ts

on

ヒントビット更新時もfu ll_p age_w rites を実行する。

実行サーバ

p g_rew in d p o stgresql.co n f

内容

実行サーバ

■p g_rew in d コマンドp g_rew in d コマンドの主なオプションは次の通りです。
表 5.31 p g_rew in d のオプション
オプショ ンD < ターゲットクラスタ >so u rce-server= "< ソースクラスタ > "内容

p g_rew in d を実行し、ソースクラスタの内容に置き換えるクラスタを指定する。
同期対象であるソースクラスタを指定します。主に次の接続文字列を使用します。

h o st ：ソースクラスタのホスト名またはIP アドレス
p o rt ：ソースクラスタのポート番号
db n ame ：ソースクラスタの接続先データベース名u ser ：ソースクラスタの接続先ユーザ
P

進行状況をレポートとして表示する。

■ フェイルバック手順※事前にマスタで関連パラメータの設定がされていることを前提とします。

Page 98 of 285

PostgreSQL Enterprise Consortium(1) マスタの正常停止
p g_rew in d を使用するには正常停止する必要があります。
停止した旧マスタを一旦起動した後、正常停止させます。
障害により正常に起動や停止ができない状態ではp g_rew in d は使用できません。
その場合は、p g_b aseb acku p を使用します。

$ pg_ctl stop -m fast(2) スレーブ(server2)の昇格
スレーブ(server2)にて実行します。
計画停止
同期レプリケーションのインスタンス障害(物理障害なし）$ pg_ctl promote(3) 旧マスタ (server1)のp o stgresql.co n f の修正
旧マスタ (server1)にて、パラメータの調整を行います。
syn ch ro n o u s_co mmit パラメータの無効化
syn ch ro n o u s_stan db y_n ames パラメータの無効化
p g_statsin fo の無効化(有効化されている場合）
同期レプリケーションの設定を無効化します。
新スレーブのsh ared_p relo ad_lib raries パラメータに p g_statsin fo が設定されている場合は、書き込みができずにエラーが発生します。
p g_statsin fo の指定を削除します。

$ vi $PGDATA/postgresql.conf[編集前]
synchronous_commit = onsynchronous_standby_names = '*'shared_preload_libraries = 'pg_stat_statements,pg_statsinfo'[編集後]
synchronous_commit = offsynchronous_standby_names = ''shared_preload_libraries = ''(4) スプリットブレイン状態状態を作るため、旧マスタ (server1)を再度起動し、データを更新する$ pg_ctl start$ psql=# select * from test;
col1-----13
2(3 rows)=# insert into test values(4);INSERT 0 1=# select * from test;
col1-----13
2
4(4 rows)(5) p g_rew in d の実行のため、旧マスタ (server1)を正常停止する。

Page 99 of 285

PostgreSQL Enterprise Consortium$ pg_ctl stop -m fast(6) 旧マスタ (server1)にて p g_rew in d の実行$ pg_rewind -D $PGDATA --source-server="host=server2 port=5432"
servers diverged at WAL location 0/D0000098 on timeline 45
rewinding from last common checkpoint at 0/D0000028 on timeline 45
Done!
(7) レプリケーションスロットの作成
新マスタ (server2)にて実行します。

$ psql=# SELECT pg_create_physical_replication_slot('slot_server2',true);
pg_create_physical_replication_slot------------------------------------(slot_server2,0/D00000C8)(1 行)-- 第2パラメータに trueを指定=# \x on
拡張表示は on です。

=# SELECT * FROM pg_replication_slots;-[ RECORD 1 ]-------+------------slot_name| slot_server2
plugin|slot_type| physicaldatoid|database|
temporary| f
active| f-- まだスロットは使用されてないため false
active_pid|
xmin|
catalog_xmin|
restart_lsn| 0/D00000C8-- trueの指定により、作成直後から restat_lsnを認識
confirmed_flush_lsn |(8) reco very.co n f の作成
旧マスタ (server1)にて実行します。
以下を設定します。

p rimary_co n n in fo パラメータに ap p licatio n _n ame を追加（任意／デフォルトはw alreceiver ）p rimary_slo t_n ame パラメータ追加
reco very_target_timelin e パラメータ追加$ cp $PGDATA/recovery.done $PGDATA/recovery.conf$ vi $PGDATA/recovery.conf
standby_mode = 'on'
primary_conninfo = 'user=repuser passfile=''/home/p101/.pgpass'' host=server2 port=5432
application_name=server1 sslmode=disable sslcompression=1 target_session_attrs=any'
primary_slot_name = 'slot_server2'
recovery_target_timeline = 'latest'(9) 新スレーブ(server1)の起動
新スレーブ(server1)を起動します。

$ pg_ctl start(10) 新スレーブ(server1)にてデータ確認
p g_rew in d により、データが正常に巻き戻されていることを確認します。

Page 100 of 285

PostgreSQL Enterprise Consortium$ psql=# select * from test;
col1-----13
2(3 rows)(11) 新マスタ (server2)のp o stgresql.co n f の修正
新マスタ (server2)にて実行します。
syn ch ro n o u s_co mmit パラメータの有効化
syn ch ro n o u s_stan db y_n ames パラメータの有効化
同期レプリケーションの設定を有効化します。

$ vi $PGDATA/postgresql.conf[編集前]
synchronous_commit = offsynchronous_standby_names = ''[編集後]
synchronous_commit = onsynchronous_standby_names = 'server1'
設定を反映$ pg_ctl reload(12) レプリケーションスロットの作成
新スレーブ1(server1)にて実行します。

$ psql=# SELECT pg_create_physical_replication_slot('slot_server1',true);
pg_create_physical_replication_slot------------------------------------(slot_server1,0/D0000028)(1 行)-- 第2パラメータに trueを指定=# \x on
拡張表示は on です。

=# SELECT * FROM pg_replication_slots;-[ RECORD 1 ]-------+------------slot_name| slot_server1
plugin|slot_type| physicaldatoid|database|
temporary| f
active| f-- まだスロットは使用されてないため false
active_pid|
xmin|
catalog_xmin|
restart_lsn| 0/D0000028-- trueの指定により、作成直後から restat_lsnを認識
confirmed_flush_lsn |(13) スレーブ2(server3)にて reco very.co n f 修正
スレーブ2(server3)にて実行します。
p rimary_co n n in fo パラメータの｢h o st= ｣部分を修正
p rimary_co n n in fo パラメータに ap p licatio n _n ame を追加（任意／デフォルトはw alreceiver ）Page 101 of 285

PostgreSQL Enterprise Consortiump rimary_slo t_n ame パラメータ追加
reco very_target_timelin e パラメータ追加$ vi $PGDATA/recovery.conf[編集前]
standby_mode = 'on'
primary_conninfo = 'user=repuser passfile=''/home/p101/.pgpass'' host=server2 port=5432
sslmode=disable sslcompression=1 target_session_attrs=any'[編集後]standby_mode = 'on'
primary_conninfo = 'user=repuser passfile=''/home/p101/.pgpass'' host=server1 port=5432
application_name=server3 sslmode=disable sslcompression=1 target_session_attrs=any'
primary_slot_name = 'slot_server1'
recovery_target_timeline = 'latest'
再起動$ pg_ctl restart(14) 新マスタ (server2)でのレプリケーション確認
p g_stat_rep licatio n s ビューを参照して、レプリケーション構成である事を確認します。
p g_rep licatio n _slo ts ビューを参照して、スロットがアクティブである事を確認します。

$ psql=# SELECT * FROM pg_stat_replication;-[ RECORD 1 ]----+-----------------------------pid| 82328
usesysid| 16384
usename| repuser
application_name | server1
client_addr| 172.16.25.111
client_hostname |client_port| 63206
backend_start| 2018-02-28 22:40:16.337761+09
backend_xmin|
state| streaming--- ストリーミング中
sent_lsn| 0/D0019538
write_lsn| 0/D0019538
flush_lsn| 0/D0019538
replay_lsn| 0/D0019538
write_lag|
flush_lag|
replay_lag|
sync_priority| 1
sync_state| sync--- 同期(15) 新スレーブ(server1)でのレプリケーション確認
p g_stat_rep licatio n s ビューを参照して、レプリケーション構成である事を確認します。
p g_rep licatio n _slo ts ビューを参照して、スロットがアクティブである事を確認します。

Page 102 of 285

PostgreSQL Enterprise Consortium$ psql=# SELECT * FROM pg_stat_replication;-[ RECORD 1 ]----+-----------------------------pid| 51369
usesysid| 16384
usename| repuser
application_name | server3
client_addr| 172.16.25.143
client_hostname |client_port| 47281
backend_start| 2018-02-28 23:02:39.56715+09
backend_xmin|
sent_lsn| 0/D0019538
write_lsn| 0/D0019538
flush_lsn| 0/D0019538
replay_lsn| 0/D0019538
write_lag|
flush_lag|
replay_lag|
sync_priority| 0
sync_state| async--- 非同期(16) マスタ、スレーブとタイムラインID が揃っていることを確認する。
タイムラインID の取得には、p g_co n tro ldata コマンドを使用する。
厳密には最新チェックポイント実行時のタイムラインID であるため、タイムラインID が揃っていないときは、マスタにてチェックポイント実行後、再確認します。

[マスタ (server2)]$ export LANG=C$ pg_controldata | grep " TimeLineID"
Latest checkpoint's TimeLineID:46[スレーブ1(server1)]$ export LANG=C$ pg_controldata | grep " TimeLineID"
Latest checkpoint's TimeLineID:46[スレーブ2(server3)]$ export LANG=C$ pg_controldata | grep " TimeLineID"
Latest checkpoint's TimeLineID:46■p g_rew in d 使用時の注意点
正常停止が必要p g_rew in d を実行するデータベースクラスタは正常終了しなければいけません。
物理障害等により正常停止できない場合、p g_rew in d は使用できません。p g_b aseb acku p を使用します。
同一タイムラインの場合は実施不可

p g_p ro mo te を実行せずに旧スレーブを新マスタにした場合、新マスタのタイムラインID は変わらないため、新マスタと旧マスタのタイムラインID は同じ状態です。
この場合は、p g_rew in d は実行できません。
実行時期と所要時間の関係

p g_rew in d によるフェイルバックの所要時間は２つの要素から構成されます。
Step 1. p g_rew in d による巻き戻し （旧マスタのW AL を使用）
Step 2. W AL 適用による追い付き （新マスタのW AL を使用）
新マスタで大量更新がある場合は、Step 1 は短時間で終了してもStep 2 で時間がかかります。
結果として、p g_b aseb acku p の方が効率が良い場合もあり得ます。

Page 103 of 285

PostgreSQL Enterprise Consortium
また新マスタ昇格時のW AL が削除されている場合は、後述するように Step 2 でエラーとなる可能性もあります。その場合はp g_b aseb acku pが必要となります。
p g_rew in d はフェイルオーバー後、あまり時間を置かずに実行する事がポイントです。
旧マスタのW AL 保管
旧マスタの巻き戻しに必要な旧マスタのW AL が削除されている場合、p g_rew in d は失敗します。
例えば旧マスタが障害により大量更新の途中で異常終了した場合などに発生します。
p g_rew in d 実行時に次のようなエラーが発生します。

could not open file "/home/pg96/pg96_data/pg_xlog/0000000D00000002000000CF": No
such file or directory
could not find previous WAL record at 2/CF000140
Failure, exiting
p g_rew in d が成功するかどうかは検証(dry-ru n オプション)にて事前に確認する事ができます。

$ pg_rewind -D $PGDATA --source-server="host=server2 port=5432" --dry-run
servers diverged at WAL position 0/5015B70 on timeline 1
rewinding from last common checkpoint at 0/5015B00 on timeline 1
Done!
| メッセージはdry-runオプションが無い場合と同じです。
| pg_rewindでエラーが発生する場合(スレーブのWAL削除）は、この検証にて確認できますが、| pg_rewindでエラーが発生しないで、後から発生する場合（マスタのWAL削除）は検知できません。
| 検証の仕様について認識下さい。

新マスタのW AL 保管

p g_rew in d 後、新スレーブは新マスタのW AL を適用することで、新マスタと同期します。
新マスタに昇格時のW AL ファイルが残っていない場合、新スレーブは追い付きができず、次のエラーがサーバログに出力され続けます。
対策としてレプリケーションスロットの有効化が有力ですが、新マスタのW AL 領域の枯渇にご注意下さい。

ERROR: requested WAL segment 0000000D00000000000000F3 has already been removed

タイムラインの巻き戻し

p g_rew in d はPo stgreSQ L9.6 からタイムラインの巻き戻しができるよになっています。
これによりスプリットブレインが発生しても、新マスタをスレーブに戻すことが可能です。

p g_rew in d が不要な場合
p g_rew in d はターゲットとソースクラスタのタイムラインID が分岐した場合に実行が必要です。
そのためタイムラインが枝分かれしなかった場合、p g_rew in d を実行する必要はありません。
例えばp g_rew in d 実行時に次のようなメッセージが出た場合、p g_rew in d は実行せずに、以降の操作を継続します。

servers diverged at WAL position 0/503A428 on timeline 2
no rewind required

5.4.4.15. スレーブ障害に よる対処
スレーブ障害時の緊急対応の必要性は、非同期モードが同期モードかによって異なります。
非同期モードの場合は、スレーブ障害がマスタの更新処理を阻害しないため、緊急対応は必要ありません。
とはいえ、シングル状態であるため早期にレプリケーション構成に復旧します。
同期モードの場合は、スレーブ障害に より マスタ の更新処理が阻害さ れ ハング状態と なり ます 。

Page 104 of 285

PostgreSQL Enterprise Consortium$ psql -h server1 -U postgres postgres -c "INSERT INTO test1_t VALUES ( 1 )"Cancel request sent
WARNING: canceling wait for synchronous replication due to user request
DETAIL: ** The transaction has already committed locally, but might not have been replicated tothe standby. **INSERT 0 1※Ctrl+Cをキーインする等、意図的にキャンセルしない限り、応答が返ってきません。
そのため、直ちに非同期に設定する必要があります。
非同期への切り替え処理は以降に示す様に再起動不要であるため、即時対応が可能です。
非同期への切り替えではなく、スレーブの再起動を試みる方法も考えられます。その方が効率的のようにも考えられます。
即座に起動できればその通りですが、起動に時間がかかる、あるいは物理的な障害で起動できない状態である事も考えられます。
そのような試行錯誤より、まずは確実にマスタのハング状態解消を優先します。
マスタの復旧後は、スレーブの復旧を試みます。
物理障害により起動できない場合は、フェイルバック処理と同様の作業を行います。

(1) スレーブの疑似障害発生
immediate オプションにて停止、またはp o stgres プロセスのkill する事で擬似障害を発生させます。
スレーブ1(server2)にて実行します。

$ pg_ctl -w -m immediate stop(2) スレーブ1(server2)の死活監視にて異常を検知
死活監視の方法には多数ありますが、ここではp g_isreadyコマンドを使用します。
p g_isreadyコマンドはクライアントツールですので、任意のノードから実行できます。

$ pg_isready -h server2 -U postgres -d postgres
server2:5432 - no response(3) 非同期モードに切り替え
syn ch ro n o u s_stan db y_n ames パラメータを'' に設定する事で、非同期の設定となります。
マスタ (server1)にて実行します。

Page 105 of 285

PostgreSQL Enterprise Consortium$ vi $PGDATA/postgresql.conf[変更前]
synchronous_standby_names = '*'[変更後]
synchronous_standby_names = ''$ pg_ctl reload
これでマスタが更新処理が可能な状態に復旧しました。

(4) スレーブ2(server3)を非同期モードで復活
reco very.co n f ファイルを修正します。
p rimary_co n n in fo パラメータの｢h o st= ｣部分を修正
p rimary_slo t_n ame パラメータが設定されている場合は、修正$ vi $PGDATA/recovery.conf[変更前]
primary_conninfo = 'user=repuser passfile=''/home/p101/.pgpass'' host=server2 port=12079
application_name=server3 sslmode=disable sslcompression=1 target_session_attrs=any'
primary_slot_name = 'slot_server2'[変更後]
primary_conninfo = 'user=repuser passfile=''/home/p101/.pgpass'' host=server1 port=12079
application_name=server3 sslmode=disable sslcompression=1 target_session_attrs=any'
primary_slot_name = 'slot_server1'$ pg_ctl restart(5) スレーブ2(server3)にてデータが伝播されることを確認
同期モードのスレーブも復活させたい場合は、フェイルバックと同様の作業を行います。

5.5. まとめ5.5.1. SR機能拡張の 歴史
Po stgreSQ L 9.0 でSR 機能が実装されて以降、メジャーバージョン毎に SR 関連の新機能を実装しています。
"ミスオペレーション"を"ミスオペ"と略記する場合があります。
"レプリケーションスロット"と表記した場合は物理型を指します。論理型の場合は明記します。
"レプリケーションスロット"を"スロット"と略記する場合があります。
表 5.32 SR 機能拡張の歴史
バー
ジョ ン

分野また は目的

概要

9.1

管理性の向上
データ保護

同期モード の実装
CO MMIT時に W AL の転送完了までを保証する。
データ保護とパフォーマンスとのトレードオフ

9.1

管理性向上
昇格処理の明瞭化

pg_ctl promote コ マンド に よる昇格

管理性向上
SR 状況把握の簡易化

pg_stat_replication ビ ューの追加
マスタでの、スレーブへのW AL 転送および適用の状況把握方法が簡易になった。w al_receiver_statu s_in terval 間隔で反映。

9.1

関連パラ メ ータ[マスタのp o stgresql.co n f]
syn ch ro n o u s_stan db y_n ames

明確なコマンドとなり分かり易くなった。
従来はtrigger_file で設定したパスに to u ch コマンド等でファイルを作成する方式であった。

Page 106 of 285[スレーブのp o stgresql.co n f]w al_receiver_statu s_in tervalPostgreSQL Enterprise Consortiumバー
ジョ ン

分野また は目的

概要

関連パラ メ ータ

管理性向上
SR 状況把握の簡易化

pg_last_xact_replay_timestamp 関数の追加

9.2

データ保護とパフォーマンス
の調整

同期方式に remote_write の追加
syn ch ro n o u s_co mmit の選択肢として、o n /o ff/lo cal に加え、remo te_w rite が追加された。
スレーブのメモリに書き込むまでを保証する。その時点でO S がハングした場合、
W AL は損失する。

9.2

高可用性の向上カ スケ ード レプリケ ーショ ンの実装
スレーブにぶら下がる2 段目のSR 構成が可能となった。
マスタの負荷を限定。

9.2

管理性の向上SR 状況把握の簡易化

pg_xlog_location_diff関数の追加

9.3

管理性の向上
役割交換の効率化

スイ ッチ バック の実装
h isto ryファイルの転送により実装

9.3

管理性の向上カ スケ ード レプリケ ーショ ンの管理性向上
カスケードのSR 構成において、スレーブの新マスタ昇格後に、スレーブとSR の継
続が可能に

9.3

障害時間の短縮

昇格処理の高速化
昇格処理にて、リカバリのみ実行し、チェックポイントを省略する事で時間短縮を実現。
従来はチェックポイントも昇格処理にて実行していた。

9.4

管理性の向上ミスオペ対策遅延レプリケ ーショ ンの実装
スレーブでの適用を一定時間(reco very_min _ap p ly_delay)遅らせる事で、マスタで発生したミスオペの伝搬を防ぐ。以下の事項に注意。
一定時間内にマスタでのミスオペを検知して対応が必要
昇格時には遅延分の適用が必要であるため時間を要する[スレーブのreco very.co n f]reco very_min _ap p ly_delay

9.4

管理性の向上W AL 保持の保証

レプリケ ーショ ンスロ ット の実装
スレーブに必要なW AL をマスタで保持し続ける事を保証。
特に複数スレーブ構成にて効果的。以下の事項に注意。
マスタのW AL 領域が溢れないように監視を検討
不要スロットは削除（残存しているとW AL を保持）

[マスタのp o stgresql.co n f]
max_rep licatio n _slo ts[スレーブのp o stgresql.co n f]
h o t_stan db y_feedb ack

9.4

論理レプリケーション

論理レプリケ ーショ ンの関数の実装
行レベルの変更内容を出力する関数が実装された。
p g_lo gical_slo t_get_ch an ges 関数
p g_lo gical_slo t_p eek_ch an ges 関数
必要な設定
w al_level をlo gical に設定
論理レプリケーションスロットを作成[マスタのp o stgresql.co n f]w al_level
max_rep licatio n _slo t

9.5

W AL 転送効率の向上

WAL 圧縮機能の実装
W AL を圧縮する事で、転送効率の向上する。

[マスタのp o stgresql.co n f]w al_co mp ressio n9.1

スレーブにて最終適用された時間を取得する[マスタのp o stgresql.co n f]
syn ch ro n o u s_co mmit

スレーブの転送や適用がどの程度遅れているかを取得するのが容易になった。
従来は p g_stat_rep licatio n ビューを参照していたが、ログの位置をバイト数に換算する計算が必要だった。
本関数により16 進の差分をバイト数として取得できる。

圧縮／解凍によるオーバーヘッドとのトレードオフだが、通常はメリットの方が大きい。
SR に特化した機能では無いが、特に次のSR 構成での適用が効果的と考えられる。
同期モードまたは完全同期モード
スレーブを遠隔地に配置（ディザスタ・リカバリ）

9.5

管理性の向上SR 構築の効率化

pg_rewind コ マンド の実装
昇格した新マスタと旧マスタを再同期する事で、SR 構成を実装。物理的な障害でない場合は、p g_rew in d にて対応可能な可能性がある。
新マスタから旧マスタへ差分バックアップを転送および旧マスタのW AL を適用。
差分バックアップの転送はp g_b aseb acku p と同様であるため、同様の設定が必要。

[マスタのp o stgresql.co n f]w al_lo g_h in ts
fu ll_p age_w rites
max_w al_sen ders
[マスタのp g_h b a.co n f]
rep licatio n 疑似データベースと
の認証設定

9.5

管理性の向上レプリケ ーショ ン関連メ ッセージの出力
ログ監視の利便性が向上。ただしあまり多くは出力されない。

Page 107 of 285[マスタのp o stgresql.co n f]lo g_rep licatio n _co mman ds

PostgreSQL Enterprise Consortiumバー
ジョ ン

分野また は目的

概要

関連パラ メ ータ

9.5

管理性の向上継続的アーカイブ

スレーブでのアーカ イ ブ出力機能
スレーブにおいて自分のW AL を出力する事で、昇格時に途切れること無く継続的なアーカイブが可能。

[スレーブのp o stgresql.co n f]arch ive_mo de

9.6

スケールアウト
参照負荷分散完全同期レプリケ ーショ ン機能の実装
スレーブへの適用完了までを保証。
スレーブ参照時にマスタと同一データが保証される事で、参照負荷分散によるスケールアウトが期待される。

[マスタのp o stgresql.co n f]
syn ch ro n o u s_co mmit

9.6

データ保護の多重化

複数同期スレーブ構成機能
従来は複数スレーブの内、同期モードが設定できるのは１台のみであったが、その制限が無くなった。
同時に複数スレーブに対して同期モードが設定できる。

[マスタのp o stgresql.co n f]
syn ch ro n o u s_stan db y_n ames

9.6

管理性の向上SR 状況把握の簡易化

pg_stat_wal_receiverビ ューの追加
従来はマスタにて p g_stat_rep licatio n ビューが参照できたが、スレーブにて
p g_stat_w al_receiver ビューが参照できる。
スレーブからレプリケーションの状況が把握が容易に。

9.6

9.6

管理性の向上
監視手法の多様化

pg_control_recovery 関数の追加管理性の向上
役割交換の簡易化

pg_rewind の機能拡張
タイムラインID の変更後にも対応できるようになった。

制御ファイル情報の内、リカバリに関する情報を取得。
従来はp g_co n tro ldata コマンドで制御ファイルの情報を取得したが、SELECT文で取得できる事で管理手法の選択肢が増えた。

マスタに適用する事でスレーブに戻す事が可能。

10.0

管理性の向上一時レプリケ ーショ ンスロ ット
レプリケーションスロットを一時的に作成できるようになった。
p g_b aseb acku p コマンド実行時のみ使用する場合に有効。
一時的なスロットかどうかはp g_rep licatio n _slo ts カタログのtemp o rary列で
判断する。

10.0

管理性の向上Quorum-based 同期レプリケ ーショ ン
syn ch ro n o u s_stan db y_n ames にてスレーブのリストに対して ANYを指定す[マスタのp o stgresql.co n f]
syn ch ro n o u s_stan db y_n ames

る事で、同期レプリケーションとなるスレーブが定足数に応じて任意に選択され
る。

10.0

W AL 対象の拡充

ハッシュイ ンデック スの WAL 出力
従来は非推奨だったハッシュインデックスの活用が増える可能性あり。
SR の機能ではないが、関連事項として掲載。

Page 108 of 285[マスタのp o stgresql.co n f]
syn ch ro n o u s_stan db y_n ames

PostgreSQL Enterprise Consortium

6. ロジカルレプリケーション
6.1. ロジカルレプリケーションの概要
6.1.1. 概要
Po stgreSQ L 10.0 よりロジカルレプリケーション機能がサポートされました。 ロジカルレプリケーションでは、テーブルデータに対する論理的な変更内容を用いて、サーバ間のデータレプリケーションを実現します。 Po stgreSQ L 9.0 よりサポートされているストリーミングレプリケーションでは、データベースを構成するファイルの変更内容を物理的に複製することで、 サーバ間のデータレプリケーションを実現します。 従来のストリーミングレプリケーションの物理的なレプリケーションと対比する形で、ロジカルレプリケーションと呼称されています。

6.1.2. 特徴
ロジカルレプリケーションは、Po stgreSQ L 9.0 よりサポートされているストリーミングレプリケーションと比較すると以下の特徴を持ちます。
任意のテーブルのみをレプリケーション可能
特定の処理(例えばINSERTのみ)のみをレプリケーション可能
異なるPo stgreSQ L バージョン間でのレプリケーション可能
レプリケーション先でテーブルデータの更新やインデックス定義が可能
ただし、レプリケーション先のテーブルデータを更新した場合や、特定の処理のみをレプリケーションする場合は、 レプリケーション元とレプリケーション先でデータが異なる状態になります。 上記のような場合にデータの整合性はユーザが保証する必要があります。

6.1.3. アー キ テクチャ
ロジカルレプリケーションでは、Pu b lish er(パブリッシャ)/Su b scrib er(サブスクライバ)モデルを採用しており、 レプリケーション元のサーバはPu b lish er 、レプリケーション先のサーバはSu b scrib er と呼称されます。 Pu b lish er 上には、レプリケーション対象とするテーブルの論理集合であるPu b licatio n (パブリケーション)を定義し、 Su b scrib er 上には、レプリケーション対象するパブリケーションとその接続情報であるSu b scrip tio n (サブスクリプション)を定義します。 ロジカルレプリケーションにおける各用語の説明を下表に記載します。
表 6.1 用語説明

N o.

用語

説明

1

Pu b lish er(パブリッシャ)

レプリケーション元となるサーバ

2

Su b scrib er(サブスクライバ)

レプリケーション先となるサーバ

3

Pu b licatio n (パブリケーション)

レプリケーション対象とするテーブルの定義。CREATE PU B LICATIO N コマンドで作成。

4

Su b scrip tio n (サブスクリプション)

レプリケーション対象するパブリケーションとその接続定義。CREATE SU B SCRIPTIO N コマンドで作成。

5

Rep licatio n Slo t(レプリケーションス
ロット)

レプリケーションの状態を保持するオブジェクト。Su b scrip tio n 作成時に、Pu b lish er 上に作成される。
デフォルトでは作成されるRep licatio n Slo t の名前は、Su b scrip tio n の名前と同じに設定される。

ロジカルレプリケーションは、下記の2 段階でレプリケーションが実施されます。

1. 初期データのスナップショット取得
2. データ変更内容の送受信によるデータ同期
動作は下図のイメージとなります。

Page 109 of 285

PostgreSQL Enterprise Consortium

Pu b lish er から Su b scrib er への「 2. データ変更内容の送受信によるデータ同期」は下図の処理で実施されます。

①. Pu b lish er のディスクに W AL が書き込まれる②. Pu b lish er のw alsen der プロセスが更新内容を論理的な変更内容(論理変更)に変換③. Su b scrib er のlo gical rep licatio n w o rker プロセスに論理変更を送信④. Su b scrib er のlo gical rep licatio n w o rker プロセスは論理変更をテーブルに適用

6.1.4. ユ ー スケー ス
ロジカルレプリケーションは以下のようなケースで有用と考えます。

1. 複数のDB サーバ上のデータを1 台のDB サーバに統合したいケース(データ分析用途など)
2. 任意のテーブルデータのみをレプリケーションさせたいケース(システム間のデータの連携など)
3. 異なるバージョン間でレプリケーションさせたいケース(バージョンアップ時のデータ移行など)
ロジカルレプリケーションの典型的な利用例がPo stgreSQ L の文書 [1] に記載されていますので、ご参照下さい。

[1] Po stgreSQ L 10.0 文書 - 第31 章 論理レプリケーション

6.1.5. 制限事項
ロジカルレプリケーションの制限事項は以下の通りです。(Po stgreSQ L 10 時点)

Page 110 of 285

PostgreSQL Enterprise Consortium
表 6.2 ロジカルレプリケーション制限事項

N o.

制限事項

補足

1

データベーススキーマおよびDDL コマンドはレプリケー
ションされない

データベーススキーマは、p g_du mp --sch ema-o n lyを利用して移行可能

2

シーケンスはレプリケーションされないシーケンスはレプリケーションされないが、シーケンスによって裏付けされた SERIAL 型や識別列のデータは、テーブルデータの一部としてレプリケーショ
ン可能3

TRU NCATE コマンドはレプリケーションされない

DELETE コマンドで回避することは可能

4

ラージオブジェクトはレプリケーションされない

通常のテーブルにデータを格納する以外の回避方法なし

5テーブル以外のオブジェクトはレプリケーションできないビュー、マテリアライズドビュー、パーティションのルートテーブル(親テーブル)、 外部テーブルはレプリケーションしようとするとエラーになる

制限事項の詳細は、Po stgreSQ L の文書 [2] に記載されていますので、ご参照下さい。

[2] Po stgreSQ L 10.0 文書 - 第31 章 論理レプリケーション 31.4. 制限事項

6.2. ロジカルレプリケーションの設定
ロジカルレプリケーションの設定手順について確認します。
ロジカルレプリケーションでは、レプリケーション対象とするテーブルを以下から選択することが可能です。

1. データベース内の全てのテーブルをレプリケーション(本書では「データベース単位の設定」と呼称)
2. データベースの任意のテーブルのみをレプリケーション(本書では「テーブル単位の設定」と呼称)
レプリケーション対象とするテーブルは、Pu b licatio n 作成時に指定します。
Pu b licatio n の作成コマンドであるCREATE PU B LICATIO N コマンドの構文は以下の通りです。

CREATE PUBLICATION name
[ FOR TABLE [ ONLY ] table_name [ * ] [, ...]| FOR ALL TABLES ][ WITH ( publication_parameter [= value] [, ... ] ) ]CREATE PU B LICATIO N コマンド実行時に「 FO R ALL TAB LES 」を指定した場合は、データベース内の全てのテーブルがレプリケーション対象になり、「 FO R TAB LE 」では、レプリケーション対象とするテーブルを指定します。
「 CREATE PU B LICATIO N 」コマンドの仕様は、Po stgreSQ L の文書 [1] に記載されていますので、ご参照下さい。

[1] Po stgreSQ L 10.0 文書 - SQ L コマンド CREATE PU B LICATIO N■ 環境情報手順は以下環境を前提に記載しております。
表 6.3 環境情報項目

説明

Po stgreSQ L バージョン 10.1
O S バージョン

Cen tO S 7.4

サーバ構成(各ホスト名)

2 サーバ(n o de1,n o de2)

6.2.1. デ ー タベ ー ス単位の 設定
6.2.1.1. 検証目的
データベース内の全てのテーブルをレプリケーションする際の設定手順について確認します。

6.2.1.2. 検証内容
本検証では、下記のレプリケーション設定においてデータベース単位での、柔軟なデータ連携(データレプリケーション)が実現可能かの検証を実施しました。

Page 111 of 285

PostgreSQL Enterprise Consortium

6.2.1.3. 検証環境

6.2.1.4. 検証手順
■ 事前準備ロジカルレプリケーションの環境を設定するためn o de1 およびn o de2 において、
Po stgreSQ L10 のインストールやデータベースクラスタ初期化・設定を実施します。

1. 環境準備該当サーバに ssh にて接続します。

(node1,node2のサーバにおいて実施)
ssh接続を用いて、該当環境へ接続
ユーザ: root
パスワード: xxxxxxx

2. ホスト名の設定
名前解決のために各サーバのホスト名を設定します。

(node1,node2のサーバにおいて実施)# vi /etc/hosts[下記をファイル末尾に追加]
192.168.56.101 node1
192.168.56.102 node2

3. 5432 ポートの解放
Po stgreSQ L 間の通信のため、5432 ポートを解放します。

(node1,node2のサーバにおいて実施)# firewall-cmd --permanent --add-port=5432/tcp# firewall-cmd --reload

以下のコマンド5432 でポートが解放されていることを確認します。

Page 112 of 285

PostgreSQL Enterprise Consortium(node1,node2のサーバにおいて実施)# firewall-cmd --list-ports
5432/tcp

4. Po stgreSQ L10 のyu m レポジトリ追加
Po stgreSQ L10 のyu m レポジトリ用のパッケージをインストールします。

(node1,node2のサーバにおいて実施)# yum install -y https://download.postgresql.org/pub/repos/yum/10/redhat/rhel-7x86_64/pgdg-centos10-10-1.noarch.rpm

以下のコマンドで該当のパッケージがインストールされたことを確認します。

(node1,node2のサーバにおいて実施)# yum list installed pgdg-centos10.noarch[省略]
インストール済みパッケージ

pgdg-centos10.noarch

10-1

installed

5. Po stgreSQ L のインストール
yu m コマンドを用いて、Po stgreSQ L をインストールします。

(node1,node2のサーバにおいて実施)# yum install postgresql10 postgresql10-server postgresql10-libs postgresql10-contrib

以下のコマンドでパッケージをインストールされたことを確認します。

(node1,node2のサーバにおいて実施)# yum list installed postgresql10 postgresql10-server postgresql10-libs postgresql10contrib[省略]
インストール済みパッケージ

postgresql10.x86_64@pgdg10-updates-testing
postgresql10-contrib.x86_64@pgdg10-updates-testing
postgresql10-libs.x86_64@pgdg10-updates-testing
postgresql10-server.x86_64@pgdg10-updates-testing

10.1-1PGDG.rhel7
10.1-1PGDG.rhel7
10.1-1PGDG.rhel7
10.1-1PGDG.rhel7

6. 環境変数の設定
p o stgres ユーザの環境変数の設定を行います。

Page 113 of 285

PostgreSQL Enterprise Consortium(node1,node2のサーバにおいて実施)# su - postgres$ vi ~/.bash_profile[下記をファイル末尾に追加]
export PATH=${PATH}:/usr/pgsql-10/bin
export PGHOME=/usr/pgsql-10
export PGDATA=/var/lib/pgsql/10/dataexport PGDATABASE=postgres
export PGPORT=5432
export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/pgsql-10/lib

以下のコマンドで環境変数の設定を確認します。($PG DATA の確認を例示しています。)(node1,node2のサーバにおいて実施)$ exitログアウト# su - postgres$ echo $PATH/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/usr/pgsql-10/bin$ echo $PGDATA/var/lib/pgsql/10/data

7. データベースクラスタの作成
データベースクラスタの作成を実施します。

(node1,node2のサーバにおいて実施)$ initdb -D $PGDATA -A trust -U postgres --no-locale --encoding UTF8

8. p o stgresql.co n f 修正
p o stgresql.co n f に以下を設定します。

(node1,node2のサーバにおいて実施)$ vi $PGDATA/postgresql.conf[下記の修正を加える]
listen_addresses = '*'# 全てのネットワークインターフェースへの接続を受け付ける設定
wal_level = 'logical'# ロジカルレプリケーションに必要なWAL出力設定
max_wal_senders = 10
# wal senderプロセス数の最大数を10に設定(デフォルト値)
max_replication_slots = 10
# レプリケーションスロットの最大数を10に設定(デフォルト値)
log_line_prefix = '[%m][%d][%h][%u][%e][%p] ' # ログメッセージの解析時に必要な情報を付与

9. p g_h b a.co n f
n o de1,n o de2 間でのデータベース接続を許可します。

(node1,node2のサーバにおいて実施)$ vi $PGDATA/pg_hba.conf[下記の修正を加える]
host
all
all
192.168.56.0/24※ 上記の設定は、SubscriberがPublisherのデータベースに接続する際に利用されます。
そのため、Publisherにのみ上記設定が必要となります。

10. Po stgreSQ L 起動
Po stgreSQ L を起動します。

Page 114 of 285

trustPostgreSQL Enterprise Consortium(node1,node2のサーバにおいて実施)$ pg_ctl start

以下のコマンドでPo stgreSQ L の起動を確認します。

(node1,node2のサーバにおいて実施)$ pg_ctl status
pg_ctl: サーバが動作中です(PID: 3517)/usr/pgsql-10/bin/postgres■ ロジカルレプリケーションの動作確認(データベース単位)
1. 動作確認用のデータベース作成
本検証で利用するデータベースを作成します。

(node1,node2のサーバにおいて実施)$ createdb logicalreptest

以下のコマンドでデータベースが作成されたことを確認します。

(node1,node2のサーバにおいて実施)$ psql -U postgres -l
List of databases
Name| Owner | Encoding | Collate | Ctype | Access privileges----------------+----------+----------+---------+-------+----------------------logicalreptest | postgres | UTF8
| C
| C
|
[省略]

2. 動作確認用のテーブル作成
作成したデータベースにテスト用のテーブルを作成します。

(node1,node2のサーバにおいて実施)$ psql -U postgres logicalreptest=# CREATE TABLE t1logical (c1 INT, PRIMARY KEY (c1));=# CREATE TABLE t2logical (c1 INT, PRIMARY KEY (c1));

以下のコマンドで作成したテーブルを確認します。

=# \dList of relations
Schema | Name
| Type | Owner--------+-----------+-------+---------public | t1logical | table | postgres
public | t2logical | table | postgres(2 rows)

3. Pu b licatio n 作成
n o de1 上にデータベース内の全テーブル(作成した lo gical とt2lo gical テーブル)をレプリケーション対象とするPu b licatio n を作成します。

Page 115 of 285

PostgreSQL Enterprise Consortium(node1のサーバにおいて実施)$ psql -U postgres logicalreptest=# CREATE PUBLICATION pub_node1_logicaldb FOR ALL TABLES;
以下のコマンドでPu b licatio n が作成されたことを確認します。
作成した p u b licatio n ではデータベース内の全テーブルをレプリケーション対象としているため、
All tab les 列がtru e であることを確認します。

(node1のサーバにおいて実施)$ psql -U postgres logicalreptest=# \dRp
List of publications
Name| Owner | All tables | Inserts | Updates | Deletes---------------------+----------+------------+---------+---------+--------pub_node1_logicaldb | postgres | t
| t
| t
| t
(1 row)

4. Su b scrip tio n 作成
n o de2 上に n o de1 上に作成した Pu b licatio n とレプリケーションするSu b scrip tio n を作成します。

(node2のサーバにおいて実施)$ psql -U postgres logicalreptest=# CREATE SUBSCRIPTION sub_node2_logicaldb CONNECTION 'host=node1 dbname=logicalreptest
port=5432 user=postgres' PUBLICATION pub_node1_logicaldb;

以下のコマンドでSu b scrip tio n が作成されたことを確認します。

(node2のサーバにおいて実施)$ psql -U postgres logicalreptest=# \dRs
List of subscriptions
Name| Owner | Enabled |Publication---------------------+----------+---------+----------------------sub_node2_logicaldb | postgres | t| {pub_node1_logicaldb}(1 row)

5. ロジカルレプリケーション簡易動作検証
テスト用に作成した 2 つのテーブルにレコードをそれぞれINSERTします。

(node1のサーバにおいて実施)$ psql -U postgres logicalreptest=# INSERT INTO t1logical VALUES (1);=# INSERT INTO t2logical VALUES (2);=# SELECT * FROM t1logical;
c1---1(1 rows)=# SELECT * FROM t2logical;
c1---2(1 rows)

Su b scrip tio n 側のテーブルにレコードがINSERTされていることを確認します。(レプリケーションされていることを確認)

Page 116 of 285

PostgreSQL Enterprise Consortium(node2のサーバにおいて実施)$ psql -U postgres logicalreptest=# SELECT * FROM t1logical;
c1---1 <-- node1と同じ結果がレプリケーションされていること(1 rows)---2 <-- node1と同じ結果がレプリケーションされていること(1 rows)

6.2.2. テー ブル 単位の 設定
6.2.2.1. 検証目的
データベース内の任意のテーブルのみをレプリケーションする際の設定手順について確認します。

6.2.2.2. 検証内容
本検証では、下記のレプリケーション設定においてテーブル単位での柔軟なデータ連携(データレプリケーション)が実現可能かの検証を実施しました。

6.2.2.3. 検証環境

6.2.2.4. 検証手順
データベース単位でレプリケーションの構築手順について確認します。

■ ロジカルレプリケーションの動作確認(テーブル単位)
1. 動作確認用のデータベース作成
本検証で利用するデータベースを作成します。

(node1,node2のサーバにおいて実施)$ createdb logicalreptest2

以下のコマンドでデータベースが作成されたことを確認します。

Page 117 of 285

PostgreSQL Enterprise Consortium(node1,node2のサーバにおいて実施)$ psql -U postgres -l
List of databases
Name| Owner | Encoding | Collate | Ctype | Access privileges-----------------+----------+----------+---------+-------+----------------------logicalreptest2 | postgres | UTF8
| C
| C
|
[省略]

2. 動作確認用のテーブル作成
作成したデータベースに検証用のテーブルを作成します。

(node1,node2のサーバにおいて実施)$ psql -U postgres logicalreptest2=# CREATE TABLE t3logical (c1 INT, PRIMARY KEY (c1));

以下のコマンドでテーブルが作成されたことを確認します。

(node1,node2のサーバにおいて実施)=# \dList of relations
Schema | Name
| Type | Owner--------+-----------+-------+---------public | t3logical | table | postgres(1 row)

3. Pu b licatio n 作成
n o de1 上に t3lo gical テーブルのみをレプリケーション対象とするPu b licatio n を作成します。 (FO R TAB LE に t3lo gical を指定)

(node1のサーバにおいて実施)$ psql -U postgres logicalreptest2=# CREATE PUBLICATION pub_node1_t3logical FOR TABLE t3logical;

以下のコマンドでPu b licatio n が作成されたことを確認します。 作成した Pu b licatio n では、All tab les 列がfalse であることを確認します。

(node1のサーバにおいて実施)$ psql -U postgres logicalreptest2=# \dRp
List of publications
Name| Owner | All tables | Inserts | Updates | Deletes---------------------+----------+------------+---------+---------+--------pub_node1_t3logical | postgres | f
| t
| t
| t
(1 row)

4. Su b scrip tio n 作成
n o de2 上に Pu b licatio n とレプリケーションするSu b scrip tio n を作成します。

(node2のサーバにおいて実施)$ psql -U postgres logicalreptest2=# CREATE SUBSCRIPTION sub_node2_t3logical CONNECTION 'host=node1 dbname=logicalreptest2
port=5432 user=postgres' PUBLICATION pub_node1_t3logical;

Page 118 of 285

PostgreSQL Enterprise Consortium
以下のコマンドでSu b scrip tio n が作成されたことを確認します。

(node2のサーバにおいて実施)$ psql -U postgres logicalreptest2=# \dRs
List of subscriptions
Name| Owner | Enabled |Publication---------------------+----------+---------+----------------------sub_node2_t3logical | postgres | t| {pub_node1_t3logical}(1 row)

5. ロジカルレプリケーション簡易動作検証
テスト用に作成したテーブルにレコードをINSERTします。

(node1のサーバにおいて実施)$ psql -U postgres logicalreptest2=# INSERT INTO t3logical VALUES (1);=# INSERT INTO t3logical VALUES (2);=# SELECT * FROM t3logical;
c1
---1
2
(2 rows)

Su b scrip tio n 側のテーブルにもレコードがINSERTされていることを確認します。(レプリケーションされていることを確認)(node2のサーバにおいて実施)$ psql -U postgres logicalreptest2=# SELECT * FROM t3logical;
c1---1<-- node1と同じ結果がレプリケーションされていること
2(2 rows)

6.2.3. レプリ ケー シ ョ ン 対象とする更新処理の 限定
6.2.3.1. 検証目的
任意のテーブルをロジカルレプリケーションの対象とした状態で特定の更新処理のみをレプリケーション対象とする際の手順を確認します。

6.2.3.2. 検証内容
本検証では、下記のレプリケーション設定において更新処理の限定が実現可能かの検証を実施しました。

6.2.3.3. 検証環境

Page 119 of 285

PostgreSQL Enterprise Consortium

6.2.3.4. 検証手順
■ 初期状態
1. レプリケーション状態の確認
現在のレプリケーション状態を確認します。

(node1,node2のサーバにおいて実施)$ psql -x -U postgres -c "SELECT * FROM pg_stat_replication"(0 rows)

2. テスト用データベース作成
本検証で利用するデータベースを作成します。

(node1,node2のサーバにおいて実施)$ createdb testlogicalre

以下のコマンドでデータベースが作成された事を確認します。

(node1,node2のサーバにおいて実施)$ psql -U postgres -l
List of databases
Name| Owner | Encoding | Collate | Ctype | Access privileges---------------+----------+----------+---------+-------+----------------------testlogicalre | postgres | UTF8
| C
| C
|
<-- 作成した DB
を確認
[省略]

3. テスト用テーブル作成
作成したデータベースにテスト用のテーブルを作成します。

(node1,node2のサーバにおいて実施)$ psql -U postgres testlogicalre=# CREATE TABLE t1logical (c1_no integer PRIMARY KEY, c2_date date);=# SELECT * FROM t1logical;
c1_no | c2_date-------+--------(0 rows)

Page 120 of 285

PostgreSQL Enterprise Consortium
以下のコマンドで作成したテーブルを確認します。

(node1,node2のサーバにおいて実施)=# \dt
List of relations
Schema | Name
| Type | Owner--------+-----------+-------+---------public | t1logical | table | postgres(1 row)■ ロジカルレプリケーションでのレプリケーション内容の変更
1. Pu b licatio n とSu b scrip tio n を作成
n o de1 に作成した t1lo gical テーブルに対し、in sert とu p date をレプリケーション対象とするPu b licatio n を作成します。

(node1のサーバにおいて実施)=# CREATE PUBLICATION pub_test_node1 FOR TABLE t1logical WITH (publish='insert,
update');<-- 指定した更新処理のみをレプリケーション=# SELECT * FROM pg_publication_tables;
pubname| schemaname | tablename----------------+------------+----------pub_test_node1 | public| t1logical(1 row)
以下のコマンドでPu b licatio n が作成された事を確認します。
作成した p u b licatio n では対象のテーブルのin sert とu p date をレプリケーション対象としているため、
In serts とU p dates 列がtru e であることを確認します。

(node1のサーバにおいて実施)=# \dRp
List of publications
Name| Owner | All tables | Inserts | Updates | Deletes----------------+----------+------------+---------+---------+--------pub_test_node1 | postgres | f
| t
| t
| f
<-- DELETEはレプリケーション対象に含めない(1 row)n o de2 に n o de1 に作成した Pu b licatio n とレプリケーションするSu b scrip tio n を作成します。

(node2のサーバにおいて実施)=# CREATE SUBSCRIPTION sub_test_node2 CONNECTION 'host=node1 dbname=testlogicalre
port=5432 user=postgres' PUBLICATION pub_test_node1;=# SELECT * FROM pg_subscription_rel;
srsubid | srrelid | srsubstate | srsublsn---------+---------+------------+----------16435 | 16430 | r| 0/181A380(1 row)

以下のコマンドでSu b scrip tio n が作成された事を確認します。

Page 121 of 285

PostgreSQL Enterprise Consortium(node2のサーバにおいて実施)# \dRsList of subscriptions
Name| Owner | Enabled | Publication----------------+----------+---------+-----------------sub_test_node2 | postgres | t| {pub_test_node1}(1 row)

2. レプリケーション状態確認
現在のレプリケーション状態を確認します。
作成した Pu b licatio n とSu b scrip tio n が正常にレプリケーションされているかを
ap p licatio n _n ame 、state 、syn c_state 項目で確認します。

(node1のサーバにおいて実施)$ psql -x -U postgres -c "SELECT * FROM pg_stat_replication"-[ RECORD 1 ]----+-----------------------------pid| 1264
usesysid| 10
usename| postgres
application_name | sub_test_node2<-- subscription_nameが表示される
client_addr| 192.168.56.102
client_hostname |client_port| 53076
backend_start| 2017-12-20 10:44:04.503952+09
backend_xmin|
state| streaming<-- streaming が表示される
sent_lsn| 0/181A380
write_lsn| 0/181A380
flush_lsn| 0/181A380
replay_lsn| 0/181A380
write_lag|
flush_lag|
replay_lag|
sync_priority| 0
sync_state| async<-- async(非同期)■ ロジカルレプリケーション動作確認
1. テーブルにデータを追加
テスト用に作成した Pu b licatio n 側のテーブルにレコードをINSERTします。

(node1のサーバにおいて実施)=# INSERT INTO t1logical VALUES (1,'2017/12/20');=# SELECT * FROM t1logical;
c1_no | c2_date-------+-----------1 | 2017-12-20(1 row)

Su b scrip tio n 側のテーブルにもレコードがINSERTされている事を確認します。(レプリケーションされている事を確認)

Page 122 of 285

PostgreSQL Enterprise Consortium(node2のサーバにおいて実施)=# SELECT * FROM t1logical;
c1_no | c2_date-------+-----------1 | 2017-12-20(1 row)

2. テーブルのデータを更新
Pu b licatio n 側のテーブルのレコードをU PDATE します。

(node1のサーバにおいて実施)=# UPDATE t1logical SET c2_date = '2017/12/25' WHERE c1_no = 1;=# SELECT * FROM t1logical;
c1_no | c2_date-------+-----------1 | 2017-12-25(1 row)

Su b scrip tio n 側のテーブルでもレコードがU PDATE されている事を確認します。(レプリケーションされている事を確認)(node2のサーバにおいて実施)=# SELECT * FROM t1logical;
c1_no | c2_date-------+-----------1 | 2017-12-25(1 row)

3. テーブルのデータを削除
Pu b licatio n 側のテーブルのレコードをDELETE します。

(node1のサーバにおいて実施)=# DELETE FROM t1logical WHERE c1_no = 1;=# SELECT * FROM t1logical;
c1_no | c2_date-------+--------(0 rows)<-- 指定のデータが削除される

Su b scrip tio n 側のテーブルではレコードがDELETE されていない事を確認します。(レプリケーションされていない事を確認)(node2のサーバにおいて実施)=# SELECT * FROM t1logical;
c1_no | c2_date-------+-----------1 | 2017-12-25<-- DELETEはレプリケーションされていない(1 row)

6.2.4. 設定するパラメー タ
ロジカルレプリケーションの構築に必要な設定について説明します。

6.2.4.1. 実行時パラ メ ータ

Page 123 of 285

PostgreSQL Enterprise Consortium
ロジカルレプリケーションに関係する実行時パラメータは以下のとおりです。
表 6.4 ロジカルレプリケーションに関係する実行時パラメータパラ メ ータ 名

パラ メ ータ の説明

設定するサーバ

w al_level

W AL に書かれる情報量。ロジカルレプリケーションでは「 lo gical 」を設定する。

ロジカルレプリケーションのPu b lish er 、
ストリーミングレプリケーションのマスタ

max_w al_sen dersw al sen der プロセスの最大数

ロジカルレプリケーションのPu b lish er 、
ストリーミングレプリケーションのマスタ

max_lo gical_rep licatio n _w o rkers

lo gical rep licatio n w o rker プロセスの最大数

ロジカルレプリケーションのSu b scrib er

max_w o rker_p ro cesses

バックグラウンドプロセスの最大数

ロジカルレプリケーションのPu b lish er 、Su b scrib er

max_syn c_w o rkers_p er_su b scrip tio n Su b scrip tio n あたりのワーカープロセスの並列度
max_rep licatio n _slo ts

レプリケーションスロットの最大数

ロジカルレプリケーションのSu b scrib er
ロジカルレプリケーションのPu b lish er
ストリーミングレプリケーションのマスタ

6.2.4.2. 実行時パラ メ ータ と 起動プロ セスの関係
いくつかの実行時パラメータはロジカルレプリケーション稼働時に起動されるプロセス数との関係を理解して設定値を決める必要があります。ここでは以下の実行時パラメー
タについて考察します。

max_w al_sen ders
max_lo gical_rep licatio n _w o rkers
max_w o rker_p ro cesses
max_syn c_w o rkers_p er_su b scrip tio n
max_rep licatio n _slo ts■ 初期状態
本章は以下の環境を利用した検証結果を元に解説します。

Page 124 of 285

PostgreSQL Enterprise Consortium
ロジカルレプリケーションは、Pu b lish er1 台、Su b scrib er1 台で構成
ロジカルレプリケーションのPu b lish er （サーバ1 。IP アドレス 192 ｡168 ｡127 ｡31 ）では、データベースクラスタ内に 1 つのデータベースを作成
p u b db : レプリケーション対象のテーブルを個別定義するPu b licatio n p u b 1 を作成
p u b 1 のレプリケーション対象としてテーブルdata1 を設定
ロジカルレプリケーションのSu b scrib er （サーバ2 。IP アドレス 192.168.127.32 ）では、データベースクラスタ内に 1 つのデータベースを作成
su b db : Pu b lish er のp u b db データベースに定義した Pu b licatio n から変更データを受け取るSu b scrip tio n su b 1 およびテーブルdata1 を作成
ここでPu b lish er 、Su b scrib er のプロセスを確認すると、以下のプロセスが起動していることがわかります。

lo gical rep licatio n lau n ch er: Pu b lish er, Su b scrib er に 1 つずつ
w al sen der: Pu b lish er に 1 つlo gical rep licatio n w o rker: は Su b scrib er に 1 つ(サーバ1において実施)$ ps aux | grep postgres
postgres 2503 0.0 0.8 389296 16492 pts/0
postgres 2504 0.0 0.1 242108 1936 ?
postgres 2506 0.0 0.1 389448 3664 ?
postgres 2507 0.0 0.1 389296 3428 ?
postgres 2508 0.0 0.3 389296 6324 ?
postgres 2509 0.0 0.1 389752 3048 ?
process
postgres 2510 0.0 0.1 244360 2204 ?
postgres 2511 0.0 0.1 389588 2492 ?
replication launcher
postgres 2513 0.0 0.3 392428 5788 ?
repusr1 192.168.127.32(40906) idle
(サーバ2において実施)$ ps aux | grep postgres
postgres 2387 0.0 0.8 389296 16492 pts/0
postgres 2388 0.0 0.1 242108 1932 ?
postgres 2390 0.0 0.2 389448 3928 ?
postgres 2391 0.0 0.1 389296 3428 ?
postgres 2392 0.0 0.3 389296 6324 ?
postgres 2393 0.0 0.1 389752 3076 ?
process
postgres 2394 0.0 0.1 244360 2200 ?
postgres 2395 0.0 0.1 389588 2756 ?
replication launcher
postgres 2398 0.0 0.3 397072 6320 ?
replication worker for subscription 16394

S
Ss
Ss
Ss
Ss
Ss

15:15
15:15
15:15
15:15
15:15
15:15

0:00 /usr/pgsql-10/bin/postgres
0:00 postgres: logger process
0:00 postgres: checkpointer process
0:00 postgres: writer process
0:00 postgres: wal writer process
0:00 postgres: autovacuum launcher

Ss
Ss

15:15
15:15

0:00 postgres: stats collector process
0:00 postgres: bgworker: logical

Ss

15:15

0:00 postgres: wal sender process

S
Ss
Ss
Ss
Ss
Ss

15:15
15:15
15:15
15:15
15:15
15:15

0:00 /usr/pgsql-10/bin/postgres
0:00 postgres: logger process
0:00 postgres: checkpointer process
0:00 postgres: writer process
0:00 postgres: wal writer process
0:00 postgres: autovacuum launcher

Ss
Ss

15:15
15:15

0:00 postgres: stats collector process
0:00 postgres: bgworker: logical

Ss

15:15

0:00 postgres: bgworker: logical■Publication,Subscription を追加し た 時の起動プロ セス
Pu b lish er,Su b scrib er にそれぞれテーブルdata2 を追加し、テーブルdata2 を複製するPu b licatio n ,Su b scrip tio n を作成します。

(サーバ1において実施)
pubdb=> CREATE TABLE data2 (c1 INT PRIMARY KEY, c2 VARCHAR(5));
pubdb=> CREATE PUBLICATION pub2 FOR TABLE data2;(サーバ2において実施)
subdb=> CREATE TABLE data2 (c1 INT PRIMARY KEY, c2 VARCHAR(5));
subdb=# CREATE SUBSCRIPTION sub2 CONNECTION 'host=192.168.127.31 dbname=pubdb user=repusr1
password=repusr1' PUBLICATION pub2;
NOTICE: created replication slot "sub2" on publisher
CREATE SUBSCRIPTION
Pu b lish er の動的統計情報ビューp g_stat_rep licatio n を見ると、今回追加したレプリケーション(RECO RD 1)元々設定済のレプリケーション(RECO RD 2)の情報が確認できます。

Page 125 of 285

PostgreSQL Enterprise Consortium(サーバ1において実施)
pubdb=# SELECT * FROM pg_stat_replication;-[ RECORD 1 ]----+-----------------------------pid| 2692
usesysid| 16385
usename| repusr1
application_name | sub2
client_addr| 192.168.127.32
client_hostname |client_port| 40909
backend_start| 2017-12-14 16:22:28.821828+09
backend_xmin|
state| streaming
sent_lsn| 0/16D7B60
write_lsn| 0/16D7B60
flush_lsn| 0/16D7B60
replay_lsn| 0/16D7B60
write_lag|
flush_lag|
replay_lag|
sync_priority| 0
sync_state| async-[ RECORD 2 ]----+-----------------------------pid| 2513
usesysid| 16385
usename| repusr1
application_name | sub1
client_addr| 192.168.127.32
client_hostname |client_port| 40906
backend_start| 2017-12-14 15:15:46.183256+09
backend_xmin|
state| streaming
sent_lsn| 0/16D7B60
write_lsn| 0/16D7B60
flush_lsn| 0/16D7B60
replay_lsn| 0/16D7B60
write_lag|
flush_lag|
replay_lag|
sync_priority| 0
sync_state| async
ここでPu b lish er 、Su b scrib er のプロセスを確認すると以下のプロセスが起動しており、w al sen der, lo gical rep licatio n w o rker がそれぞれSu b scrip tio n 単位に起動していることがわかります。

lo gical rep licatio n lau n ch er: Pu b lish er, Su b scrib er に 1 つずつで変化なし
w al sen der: Pu b lish er に 2 つlo gical rep licatio n w o rker: Su b scrib er に 2 つPage 126 of 285

PostgreSQL Enterprise Consortium(サーバ1において実施)$ ps aux | grep postgres
postgres 2503 0.0 0.8 389296 16492 pts/0
postgres 2504 0.0 0.1 242108 1936 ?
postgres 2506 0.0 0.2 389448 4188 ?
postgres 2507 0.0 0.1 389296 3428 ?
postgres 2508 0.0 0.3 389296 6324 ?
postgres 2509 0.0 0.1 389752 3048 ?
process
postgres 2510 0.0 0.1 244360 2204 ?
postgres 2511 0.0 0.1 389588 2492 ?
replication launcher
postgres 2513 0.0 0.3 392428 5788 ?
repusr1 192.168.127.32(40906) idle
postgres 2692 0.0 0.2 392292 5252 ?
repusr1 192.168.127.32(40909) idle
(サーバ2において実施)$ ps aux | grep postgres
postgres 2387 0.0 0.8 389296 16492 pts/0
postgres 2388 0.0 0.1 242108 1932 ?
postgres 2390 0.0 0.2 389448 4192 ?
postgres 2391 0.0 0.1 389296 3428 ?
postgres 2392 0.0 0.3 389296 6324 ?
postgres 2393 0.0 0.1 389752 3076 ?
process
postgres 2394 0.0 0.1 244360 2200 ?
postgres 2395 0.0 0.1 389588 2756 ?
replication launcher
postgres 2398 0.0 0.3 397072 6320 ?
replication worker for subscription 16394
postgres 2571 0.0 0.3 397072 5992 ?
replication worker for subscription 16400

S
Ss
Ss
Ss
Ss
Ss

15:15
15:15
15:15
15:15
15:15
15:15

0:00 /usr/pgsql-10/bin/postgres
0:00 postgres: logger process
0:00 postgres: checkpointer process
0:00 postgres: writer process
0:00 postgres: wal writer process
0:00 postgres: autovacuum launcher

Ss
Ss

15:15
15:15

0:00 postgres: stats collector process
0:00 postgres: bgworker: logical

Ss

15:15

0:00 postgres: wal sender process

Ss

16:22

0:00 postgres: wal sender process

S
Ss
Ss
Ss
Ss
Ss

15:15
15:15
15:15
15:15
15:15
15:15

0:00 /usr/pgsql-10/bin/postgres
0:00 postgres: logger process
0:00 postgres: checkpointer process
0:00 postgres: writer process
0:00 postgres: wal writer process
0:00 postgres: autovacuum launcher

Ss
Ss

15:15
15:15

0:00 postgres: stats collector process
0:00 postgres: bgworker: logical

Ss

15:15

0:00 postgres: bgworker: logical

Ss

16:22

0:00 postgres: bgworker: logical■ 初期データ コ ピー実行中の起動プロ セス
次にロジカルレプリケーションのSu b scrip tio n 作成時に実行される初期データのコピー処理による起動プロセスの増減を確認します。ここではベンチマークツールp gb en chを使ってあらかじめデータベースp u b db に 1000 万件の初期データを持つテーブルp gb en ch _acco u n ts を作成します。

(サーバ1において実施)$ pgbench -i -s 10 -U pubusr1 pubdb
Pu b lish er のデータベースp u b db に接続し、新たなPu b licatio n p u b 3 を作成します。

(サーバ1において実施)
pubdb=> CREATE PUBLICATION pub3 FOR TABLE pgbench_accounts;
Su b scrib er のデータベースsu b db に接続し、レプリケーション対象テーブルを作成します。

(サーバ2において実施)
subdb=> CREATE TABLE pgbench_accounts (aid INT PRIMARY KEY, bid INT, abalance INT, filler
character(84));
データベースsu b db に Su b scrip tio n su b 3 を作成し、ロジカルレプリケーションを開始します。

(サーバ2において実施)
subdb=# CREATE SUBSCRIPTION sub3 CONNECTION 'host=192.168.127.31 dbname=pubdb user=repusr1
password=repusr1' PUBLICATION pub3;
Su b scrip tio n su b 3 を作成した直後に Pu b lish er,Su b scrib er の起動プロセスを確認すると、w al sen der, lo gical rep licatio n w o rker がそれぞれSu b scrip tio n に加えて初期データコピーを実行するプロセス分増えていることがわかります。

lo gical rep licatio n lau n ch er: Pu b lish er, Su b scrib er に 1 つずつで変化なし

Page 127 of 285

PostgreSQL Enterprise Consortium

w al sen der: Pu b lish er に 4 つ。うち1 つは初期データコピー用のプロセス
lo gical rep licatio n w o rker: Su b scrib er に 4 つ。うち1 つは初期データコピー用のプロセス(サーバ1において実施)$ ps aux | grep postgres
postgres 2503 0.0 0.6 389296 12532 ?
postgres 2504 0.0 0.0 242108 1576 ?
postgres 2506 0.0 2.2 389612 42160 ?
postgres 2507 0.0 0.1 389296 2892 ?
postgres 2508 0.0 0.3 389296 5836 ?
postgres 2509 0.0 0.1 389752 2564 ?
process
postgres 2510 0.0 0.0 244360 1816 ?
postgres 2511 0.0 0.1 389588 2120 ?
replication launcher
postgres 2513 0.0 1.9 425924 36336 ?
repusr1 192.168.127.32(40906) idle
postgres 2692 0.0 1.9 425852 35920 ?
repusr1 192.168.127.32(40909) idle
postgres 5731 0.0 0.2 392292 5248 ?
repusr1 192.168.127.32(40958) idle
postgres 5732 19.8 0.4 392592 8788 ?
repusr1 192.168.127.32(40959) COPY

S
Ss
Ss
Ss
Ss
Ss

12月14
12月14
12月14
12月14
12月14
12月14

0:01 /usr/pgsql-10/bin/postgres
0:00 postgres: logger process
0:00 postgres: checkpointer process
0:00 postgres: writer process
0:02 postgres: wal writer process
0:00 postgres: autovacuum launcher

Ss
Ss

12月14
12月14

0:02 postgres: stats collector process
0:00 postgres: bgworker: logical

Ss

12月14

0:24 postgres: wal sender process

Ss

12月14

0:24 postgres: wal sender process

Ss

10:15

0:00 postgres: wal sender process

Ss

10:15

0:01 postgres: wal sender process(サーバ2において実施)$ ps aux | grep postgres
postgres 2387 0.0 0.8 389296 16492 ?
S
12月14
postgres 2388 0.0 0.1 242108 2000 ?
Ss 12月14postgres 2390 0.0 2.3 389612 43740 ?
Ss 12月14postgres 2391 0.0 0.1 389296 3428 ?
Ss 12月14postgres 2392 0.0 0.3 389296 6324 ?
Ss 12月14postgres 2393 0.0 0.1 389752 3092 ?
Ss 12月14process
postgres 2394 0.0 0.1 244360 2200 ?
Ss 12月14postgres 2395 0.0 0.1 389588 2756 ?
Ss 12月14replication launcher
postgres 2398 0.0 0.3 397072 6320 ?
Ss 12月14replication worker for subscription 16394
postgres 2571 0.0 0.3 397072 5992 ?
Ss 12月14replication worker for subscription 16400
postgres 5597 0.0 0.3 397072 5728 ?
Ss 10:15
replication worker for subscription 16418
postgres 5598 83.0 3.5 397536 67052 ?
Rs 10:15
replication worker for subscription 16418 sync 16413

0:01 /usr/pgsql-10/bin/postgres
0:00 postgres: logger process
0:00 postgres: checkpointer process
0:00 postgres: writer process
0:01 postgres: wal writer process
0:00 postgres: autovacuum launcher
0:02 postgres: stats collector process
0:00 postgres: bgworker: logical
0:03 postgres: bgworker: logical
0:03 postgres: bgworker: logical
0:00 postgres: bgworker: logical
0:06 postgres: bgworker: logical

なお、初期データのコピーが完了するとコピー用のプロセス(上記のPID 5732,5598)が終了し、w al sen der,lo gical rep licatio n w o rker の数がSu b scrip tio n と同じ3つに変化することも確認できました。

■max_worker_processes と 起動プロ セスの関係
max_w o rker_p ro cesses はバックグラウンドプロセスの最大数を定義する実行時パラメータでデフォルト値は8 です。ロジカルレプリケーションに関係するプロセスもバックグラ
ウンドプロセスとして起動するため、このパラメータとの関係を検証します。
まず、Su b scrib er のmax_w o rker_p ro cesses を3 に変更して再起動すると、サーバログに以下の様なエラーメッセージが出力されます。

(サーバ2のサーバログ)
2017-12-15 11:00:44.951 JST [5755] LOG: logical replication apply worker for subscription "sub1"
has started
2017-12-15 11:00:44.963 JST [5756] LOG: logical replication apply worker for subscription "sub2"
has started
2017-12-15 11:00:44.966 JST [5754] WARNING: out of background worker slots
2017-12-15 11:00:44.966 JST [5754] HINT: You might need to increase max_worker_processes.
この時、Su b scrib er の動的統計情報ビューp g_stat_activityでバックグラウンドプロセスの情報を表示すると、lo gical rep licatio n lau n ch er と lo gical rep licatio n
w o rker がそれぞれバックグラウンドプロセスとして起動していることがわかります。

Page 128 of 285

PostgreSQL Enterprise Consortium(サーバ2において実施)
postgres=# select datname,pid,usename,backend_start,wait_event_type,wait_event,state,backend_typefrom pg_stat_activity where backend_type = 'background worker';
datname | pid | usename |backend_start| wait_event_type |wait_event| state | backend_type---------+------+----------+-------------------------------+-----------------+--------------------+-------+------------------| 5790 | postgres | 2017-12-15 11:01:16.733998+09 | Activity| LogicalLauncherMain|| background worker
subdb | 5791 | postgres | 2017-12-15 11:01:16.742653+09 | Activity| LogicalApplyMain| idle | background worker
subdb | 5792 | postgres | 2017-12-15 11:01:16.750161+09 | Activity| LogicalApplyMain| idle | background worker
subdb | 5793 | postgres | 2017-12-15 11:01:16.760481+09 | Activity| LogicalApplyMain| idle | background worker
一方でPu b lish er のmax_w o rker_p ro cesses を3 に変更して再起動してもサーバログにエラーメッセージは出力されません。この時のPu b lish er のバックグラウンドプロセスの情報を表示すると、lo gical rep licatio n lau n ch er だけがカウントされ、w al sen der はカウントされていません。

(サーバ1において実施)
pubdb=# select datname,pid,usename,backend_start,wait_event_type,wait_event,state,backend_typefrom pg_stat_activity where backend_type = 'background worker';
datname | pid | usename |backend_start| wait_event_type |wait_event| state | backend_type---------+------+----------+-------------------------------+-----------------+--------------------+-------+------------------| 2511 | postgres | 2017-12-14 15:15:28.478386+09 | Activity| LogicalLauncherMain|| background worker
このように、Pu b lish er のw al sen der とSu b scrib er のlo gical rep licatio n w o rker はともに Su b scrip tio n の数に合わせて増加しますが、Pu b lish er のw al sen der は
max_w o rker_p ro cesses で管理するバックグラウンドプロセスとは異なる扱いであることがわかりました。

6.2.4.3. 考察
検証結果を踏まえて以下の実行時パラメータの決め方について考察します。

■max_wal_senders
ロジカルレプリケーションでの起動数とストリーミングレプリケーションのスレーブ数を考慮して以下の数が必要です。
必要数 = (ストリーミングレプリケーションのスレーブ数) + (ロジカルレプリケーションのSu b scrip tio n 数 ✕ 2)
ロジカルレプリケーションの初期データ同期プロセスが起動する分を考慮して 2 倍する。
初期データ同期プロセスは max_syn c_w o rkers_p er_su b scrip tio n （デフォルト2 ）に応じて変化するため、ここを増やす場合は✕2 の部分も変化する。
Po stgreSQ L10.1 のデフォルト値は10 。上記必要数で不足しそうなら変更しておく。

■max_logical_replication_workers
ロジカルレプリケーションでの起動数を考慮して以下の数が必要です。
必要数 = このサーバに作成するSu b scrip tio n の数 ✕ 2
ロジカルレプリケーションの初期データ同期時に増えるプロセス数を考慮して 2 倍する。
初期データ同期プロセスは max_syn c_w o rkers_p er_su b scrip tio n （デフォルト2 ）に応じて変化するため、ここを増やす場合は✕2 の部分も変化する。
Po stgreSQ L10.1 のデフォルト値は4 。上記必要数で不足しそうなら変更しておく。

■max_worker_processes
ロジカルレプリケーションでの起動数を考慮して以下の数が必要です。
必要数 = このサーバに作成するSu b scrip tio n の数 ✕ 2 + 1(= lo gical rep licatio n lau n ch er の分) + その他のバックグラウンドプロセス(任意)初期データ同期プロセスは max_syn c_w o rkers_p er_su b scrip tio n （デフォルト2 ）に応じて変化するため、ここを増やす場合は✕2 の部分も変化する。
Po stgreSQ L10.1 のデフォルト値は8 。上記必要数で不足しそうなら変更しておく。

■max_sync_workers_per_subscription
Po stgreSQ L10.1 のデフォルト値は2 で、この場合1 つがレプリケーション用に確保されるため初期データ同期の並列度は1 で固定されます。
初期データ同期の並列度は1 テーブルにつき1 までです。複数テーブルに対する同期処理の並列度は上がりますが、同じテーブルに対する並列度は上がりません。
■max_replication_slots
Page 129 of 285

PostgreSQL Enterprise Consortium

ロジカルレプリケーションでの作成数と合わせて以下の数が必要です。
必要数 = ロジカルレプリケーションで作成するSu b scrip tio n の数 ✕ 2 + ストリーミングレプリケーションで作成するレプリケーションスロットの数(任意)
初期データ同期プロセスは一時的にレプリケーションスロットを作成するため、ここを増やす場合は✕2 の部分も変化する。
Po stgreSQ L10.1 のデフォルト値は10 。上記必要数で不足しそうなら変更しておく。

6.3. ロジカルレプリケーションの応用
6.3.1. primary keyが定義され てい ない テー ブル の レプリ ケー シ ョ ン 設定
6.3.1.1. 検証目的
p rimary keyが定義されていないテーブルにおけるロジカルレプリケーションの設定方法を確認します。
ロジカルレプリケーションでは、U PDATE もしくはDELTE をレプリケーションさせるために、 変更したレコードを特定するための情報であるREPLICA IDENTITYをPu b licatio n 側のテーブルに設定する必要があります。 REPLICA IDENTITYが指定されていないテーブルがPu b licatio n に追加された場合、U PDATE もしくはDELETE が行われるとエラーが発生します。
デフォルトではp rimary keyが定義されたテーブルの場合、p rimary keyがREPLICA IDENTITYとして自動設定されます。 また、REPLICA IDENTITYにユニークインデックス(NO T NU LL かつ部分インデックスや遅延可能インデックスではない)を設定することで、U PDATE とDELETE のレプリケーションが可能です。 テーブルに上記のキーが存在しない場合は、REPLICA IDENTITYに FU LL を設定することで、U PDATE とDELETE のレプリケーションが可能です。[1]REPLICA IDENTITYは、ロジカルレプリケーション利用時において、W AL ファイルに書き込む情報量に影響します。 REPLICA IDENTITYに指定された列では、変更前の古いレコードがW AL ファイルに書き込まれます。 FU LL を指定した場合には、古いレコードを全て W AL ファイルに書き込みため、非効率です。
REPLICA IDENTITYの設定方法の詳細は、Po stgreSQ L 文書をご確認下さい。[2][1] Po stgreSQ L 10.0 文書 - 第31 章 論理レプリケーション 31.1. パブリケーション[2] Po stgreSQ L 10.0 文書 - SQ L コマンド ALTER TAB LE
6.3.1.2. 検証内容
p rimary keyが定義されていないテーブルにおけるレプリケーションが可能な否かを確認するため、 下表のパターンでロジカルレプリケーションの設定を実施しました。 パター
ンと動作確認結果は下表の通りです。(○: レプリケーション可能、× : レプリケーション不可)
表 6.5 p rimary keyが定義されていないテーブルの動作確認
テーブル構成IN SERT

UPDATE

備考

DELETEp rimary keyが定義されたテーブル

○

○

○

-

u n iqu e 制約(n o t n u ll 制約)とrep lica
iden tityに u n iqu e インデックスを指定

○

○

○

-

上記に該当しないテーブル

○

×

×

Pu b licatio n 側のテーブルに DELETE お
よびU PDATE を実施した場合、エラーになる

上記に該当しないテーブルに rep lica
iden tityに fu ll を指定○○

6.3.1.3. 検証環境

Page 130 of 285○-

PostgreSQL Enterprise Consortium

6.3.1.4. 検証手順
1. 動作確認用のテーブル作成
テスト用のテーブルを作成します。

(node1,node2のサーバにおいて実施)$ psql -U postgres logicalreptest3=# CREATE TABLE t8logical (c1 INT, c2 TEXT, PRIMARY KEY (c1));=# CREATE TABLE t9logical (c1 INT NOT NULL, c2 TEXT);=# CREATE UNIQUE INDEX t9logical_ukey ON t9logical (c1);=# ALTER TABLE t9logical REPLICA IDENTITY USING INDEX t9logical_ukey;=# CREATE TABLE t10logical (c1 INT, c2 TEXT);=# CREATE TABLE t11logical (c1 INT, c2 TEXT);=# ALTER TABLE t11logical REPLICA IDENTITY FULL;

以下のコマンドでテーブルが作成されたことを確認します。

(node1,node2のサーバにおいて実施)=# \dList of relations
Schema |Name| Type | Owner--------+------------+-------+---------public | t10logical | table | postgres
public | t11logical | table | postgres
public | t8logical | table | postgres
public | t9logical | table | postgres(4 rows)

2. Pu b licatio n 作成
n o de1 上の各テーブルをレプリケーション対象とするPu b licatio n を作成します。

Page 131 of 285

PostgreSQL Enterprise Consortium(node1のサーバにおいて実施)$ psql -U postgres logicalreptest3=# CREATE PUBLICATION pub_node1_t8logical FOR TABLE t8logical;=# CREATE PUBLICATION pub_node1_t9logical FOR TABLE t9logical;=# CREATE PUBLICATION pub_node1_t10logical FOR TABLE t10logical;=# CREATE PUBLICATION pub_node1_t11logical FOR TABLE t11logical;

以下のコマンドでPu b licatio n が作成されたことを確認します。

(node1のサーバにおいて実施)=# \dRp
List of publications
Name| Owner | All tables | Inserts | Updates | Deletes----------------------+----------+------------+---------+---------+--------pub_node1_t10logical | postgres | f
| t
| t
| t
pub_node1_t11logical | postgres | f
| t
| t
| t
pub_node1_t8logical | postgres | f
| t
| t
| t
pub_node1_t9logical | postgres | f
| t
| t
| t
(5 rows)

3. Su b scrip tio n 作成
n o de2 上に n o de1 に作成した Pu b licatio n とレプリケーションするSu b scrip tio n を作成します。

(node2のサーバにおいて実施)$ psql -U postgres logicalreptest3=# CREATE SUBSCRIPTION sub_node2_t8logical CONNECTION 'host=node1 dbname=logicalreptest3
port=5432 user=postgres' PUBLICATION pub_node1_t8logical;=# CREATE SUBSCRIPTION sub_node2_t9logical CONNECTION 'host=node1 dbname=logicalreptest3
port=5432 user=postgres' PUBLICATION pub_node1_t9logical;=# CREATE SUBSCRIPTION sub_node2_t10logical CONNECTION 'host=node1
dbname=logicalreptest3 port=5432 user=postgres' PUBLICATION pub_node1_t10logical;=# CREATE SUBSCRIPTION sub_node2_t11logical CONNECTION 'host=node1
dbname=logicalreptest3 port=5432 user=postgres' PUBLICATION pub_node1_t11logical;

以下のコマンドでSu b scrip tio n が作成されたことを確認します。

(node2のサーバにおいて実施)=# \dRs
List of subscriptions
Name| Owner | Enabled |Publication----------------------+----------+---------+-----------------------sub_node2_t10logical | postgres | t| {pub_node1_t10logical}
sub_node2_t11logical | postgres | t| {pub_node1_t11logical}
sub_node2_t8logical | postgres | t| {pub_node1_t8logical}
sub_node2_t9logical | postgres | t| {pub_node1_t9logical}(4 rows)

4. ロジカルレプリケーション動作検証確認
p rimary keyが定義されたテーブルに対して、INSERT,U PDATE,DELETE を実施します。

Page 132 of 285

PostgreSQL Enterprise Consortium(node1のサーバにおいて実施)$ psql -U postgres logicalreptest3=# INSERT INTO t8logical VALUES (1, 'MERRY CHRISTMAS');=# INSERT INTO t8logical VALUES (2, 'CHRISTMAS DAY');=# UPDATE t8logical SET c2 = 'HAPPY NEW YEAR' WHERE c1 = 2;=# DELETE FROM t8logical WHERE c1 = 1;=# SELECT * FROM t8logical;
c1 |c2----+---------------2 | HAPPY NEW YEAR
(1 row)

Su b scrip tio n 側にデータが反映されていることを確認します。

(node1のサーバにおいて実施)$ psql -U postgres logicalreptest3=# SELECT * FROM t8logical;
c1 |c2----+---------------2 | HAPPY NEW YEAR
(1 row)

u n iqu e 制約(n o t n u ll 制約)とrep lica iden tityに u n iqu e インデックスが定義されたテーブルに対して、INSERT,U PDATE,DELETE を実施します。

(node1のサーバにおいて実施)$ psql -U postgres logicalreptest3=# INSERT INTO t9logical VALUES (1, 'MERRY CHRISTMAS');=# INSERT INTO t9logical VALUES (2, 'CHRISTMAS DAY');=# UPDATE t9logical SET c2 = 'HAPPY NEW YEAR' WHERE c1 = 2;=# DELETE FROM t9logical WHERE c1 = 1;=# SELECT * FROM t9logical;
c1 |c2----+---------------2 | HAPPY NEW YEAR
(1 row)

Su b scrip tio n 側にデータが反映されていることを確認します。

(node1のサーバにおいて実施)$ psql -U postgres logicalreptest3=# SELECT * FROM t9logical;
c1 |c2----+---------------2 | HAPPY NEW YEAR
(1 row)

上記に該当しないテーブルに対して、INSERT,U PDATE,DELETE を実施します。

Page 133 of 285

PostgreSQL Enterprise Consortium(node1のサーバにおいて実施)$ psql -U postgres logicalreptest3=# INSERT INTO t10logical VALUES (1, 'MERRY CHRISTMAS');=# INSERT INTO t10logical VALUES (2, 'CHRISTMAS DAY');=# UPDATE t10logical SET c2 = 'HAPPY NEW YEAR' WHERE c1 = 2;
ERROR: cannot update table "t10logical" because it does not have a replica identity andpublishes updates
HINT: To enable updating the table, set REPLICA IDENTITY using ALTER TABLE.
※ REPLICA IDENTITYが指定されていない場合は、UPDATEできない旨がメッセージとして出力される。
=# DELETE FROM t10logical WHERE c1 = 1;
ERROR: cannot delete from table "t10logical" because it does not have a replica
identity and publishes deletes
HINT: To enable deleting from the table, set REPLICA IDENTITY using ALTER TABLE.
=# SELECT * FROM t10logical;
c1 |c2----+----------------1 | MERRY CHRISTMAS
2 | CHRISTMAS DAY(2 rows)

Su b scrip tio n 側にデータが反映されないことを確認します。

(node1のサーバにおいて実施)$ psql -U postgres logicalreptest3=# SELECT * FROM t10logical;
c1 |c2----+----------------1 | MERRY CHRISTMAS
2 | CHRISTMAS DAY(2 rows)

上記に該当しないテーブルに rep lica iden tityに fu ll を指定したテーブルに対して、INSERT,U PDATE,DELETE を実施します。

(node1のサーバにおいて実施)$ psql -U postgres logicalreptest3=# INSERT INTO t11logical VALUES (1, 'MERRY CHRISTMAS');=# INSERT INTO t11logical VALUES (2, 'CHRISTMAS DAY');=# UPDATE t11logical SET c2 = 'HAPPY NEW YEAR' WHERE c1 = 2;=# DELETE FROM t11logical WHERE c1 = 1;=# SELECT * FROM t11logical;
c1 |c2----+---------------2 | HAPPY NEW YEAR
(1 row)

Su b scrip tio n 側にデータが反映されていることを確認します。

(node1のサーバにおいて実施)$ psql -U postgres logicalreptest3=# SELECT * FROM t11logical;
c1 |c2----+---------------2 | HAPPY NEW YEAR
(1 row)

6.3.2. 同期レプリ ケー シ ョ ン
6.3.2.1. 検証目的
Page 134 of 285

PostgreSQL Enterprise Consortium

6.3.2.1. 検証目的
ロジカルレプリケーションにおいて同期レプリケーションを設定する手順を確認します。

6.3.2.2. 検証内容
本検証では、下記のレプリケーション設定において同期モードが実現可能かの検証を実施しました。

6.3.2.3. 検証環境

6.3.2.4. 検証手順
■ 初期状態
1. レプリケーション状態の確認
現在のレプリケーション状態を確認します。

(node1,node2のサーバにおいて実施)$ psql -x -U postgres -c "SELECT * FROM pg_stat_replication"(0 rows)

2. テスト用データベース作成
本検証で利用するデータベースを作成します。

(node1,node2のサーバにおいて実施)$ createdb testlogicaldb

以下のコマンドでデータベースが作成された事を確認します。

(node1,node2のサーバにおいて実施)$ psql -U postgres -l
List of databases
Name| Owner | Encoding | Collate | Ctype | Access privileges---------------+----------+----------+---------+-------+----------------------testlogicaldb | postgres | UTF8
| C
| C
|
<-- 作成した DB
を確認
[省略]

3. テスト用テーブル作成
作成したデータベースにテスト用のテーブルを作成します。

Page 135 of 285

PostgreSQL Enterprise Consortium(node1,node2のサーバにおいて実施)$ psql -U postgres testlogicaldb=# CREATE TABLE t1logical (c1_no integer PRIMARY KEY, c2_date_time timestamp);=# SELECT * FROM t1logical;
c1_no | c2_date_time-------+-------------(0 rows)

以下のコマンドで作成したテーブルを確認します。

(node1,node2のサーバにおいて実施)=# \dt
List of relations
Schema | Name
| Type | Owner--------+-----------+-------+---------public | t1logical | table | postgres(1 row)■ ロジカルレプリケーションでの作成
1. Pu b licatio n とSu b scrip tio n を作成
n o de1 に作成した t1lo gical テーブルをレプリケーション対象とするPu b licatio n を作成します。

(node1のサーバにおいて実施)=# CREATE PUBLICATION test_slot1 FOR TABLE t1logical;=# SELECT * FROM pg_publication_tables;
pubname | schemaname | tablename------------+------------+----------test_slot1 | public| t1logical(1 row)

以下のコマンドでPu b licatio n が作成された事を確認します。

=# \dRp
List of publications
Name| Owner | All tables | Inserts | Updates | Deletes------------+----------+------------+---------+---------+--------test_slot1 | postgres | f
| t
| t
| t

<-- オールテーブ

ルではない(1 row)n o de2 に n o de1 に作成した Pu b licatio n とレプリケーションするSu b scrip tio n を作成します。

(node2のサーバにおいて実施)=# CREATE SUBSCRIPTION test_slot1 CONNECTION 'host=node1 dbname=testlogicaldb port=5432
user=postgres' PUBLICATION test_slot1;=# SELECT * FROM pg_subscription_rel;
srsubid | srrelid | srsubstate | srsublsn---------+---------+------------+-----------24755 | 24750 | r| 2/912A8A10(1 row)

以下のコマンドでSu b scrip tio n が作成された事を確認します。

Page 136 of 285

PostgreSQL Enterprise Consortium# \dRsList of subscriptions
Name| Owner | Enabled | Publication------------+----------+---------+-------------test_slot1 | postgres | t| {test_slot1}(1 row)

2. レプリケーション状態確認
現在のレプリケーション状態を確認します。
作成した Pu b licatio n とSu b scrip tio n が正常にレプリケーションされているかを
ap p licatio n _n ame 、state 、syn c_state 項目で確認します。(syn c_state は現時点ではasyn c となります。)(node1のサーバにおいて実施)$ psql -x -U postgres -c "SELECT * FROM pg_stat_replication"-[ RECORD 1 ]----+-----------------------------pid| 1667
usesysid| 10
usename| postgres
application_name | test_slot1<-- subscription_nameが表示される
client_addr| 192.168.43.11
client_hostname |client_port| 35564
backend_start| 2018-01-05 10:39:02.914132+09
backend_xmin|
state| streaming<-- streaming が表示される
sent_lsn| 2/912A8A10
write_lsn| 2/912A8A10
flush_lsn| 2/912A8A10
replay_lsn| 2/912A8A10
write_lag|
flush_lag|
replay_lag|
sync_priority| 0
sync_state| async<-- async(現時点では非同期)■ 同期レプリケーションの設定
1. コマンドラインで設定
コマンドラインからレプリケーションの設定を同期レプリケーションにします。

(node1のサーバにおいて実施)$ psql -U postgres testlogicaldb=# ALTER SYSTEM SET synchronous_standby_names = 'test_slot1';
application_nameを指定する

設定ファイルで同期レプリケーションに変更された事を確認します。

(node1のサーバにおいて実施)$ cat /var/lib/pgsql/10/data/postgresql.auto.conf# Do not edit this file manually!
# It will be overwritten by ALTER SYSTEM command.
synchronous_standby_names = 'test_slot1'<-- 同期設定に application_nameが設定されている

Page 137 of 285<--

PostgreSQL Enterprise Consortium
設定ファイルの内容を反映します。

(node1のサーバにおいて実施)$ pg_ctl reload

Po stgreSQ L 起動時に syn ch ro n o u s_stan db y_n ames パラメータが読み込まれている事を確認します。

(nodeのサーバにおいてサーバ実施)$ tail /var/lib/pgsql/10/data/log/postgresql-2018-01-05.log
読み込まれていることを確認

LOG: received SIGHUP, reloading configuration files
LOG: parameter "synchronous_standby_names" changed to "test_slot1"
LOG: standby "test_slot1" is now a synchronous standby with priority 1
現在のレプリケーション状態を確認します。
syn c_state がsyn c となっている事を確認します。

(node1のサーバにおいて実施)$ psql -x -U postgres -c "SELECT * FROM pg_stat_replication"-[ RECORD 1 ]----+-----------------------------pid| 1667
usesysid| 10
usename| postgres
application_name | test_slot1
client_addr| 192.168.43.11
client_hostname |client_port| 35564
backend_start| 2018-01-05 10:39:02.914132+09
backend_xmin|
state| streaming
sent_lsn| 2/912A8A10
write_lsn| 2/912A8A10
flush_lsn| 2/912A8A10
replay_lsn| 2/912A8A10
write_lag|
flush_lag|
replay_lag|
sync_priority| 1
sync_state| sync<-- sync(同期)になる■ ロジカルレプリケーション動作確認
1. テーブルにデータを追加
テスト用に作成した Pu b licatio n 側のテーブルにレコードをINSERTします。

(node1のサーバにおいて実施)$ psql -U postgres testlogicaldb=# INSERT INTO t1logical VALUES (1,localtimestamp);=# SELECT * FROM t1logical;
c1_no |
c2_date_time-------+---------------------------1 | 2018-01-05 11:07:36.590471(1 row)

Su b scrip tio n 側のテーブルにもレコードがINSERTされている事を確認します。(レプリケーションされている事を確認)

Page 138 of 285<-- ログで設定がPostgreSQL Enterprise Consortium(node2のサーバにおいて実施)=# SELECT * FROM t1logical;
c1_no |
c2_date_time-------+---------------------------1 | 2018-01-05 11:07:36.590471(1 row)

2. テーブルのデータを更新
Pu b licatio n 側のテーブルのレコードをU PDATE します。

(node1のサーバにおいて実施)=# UPDATE t1logical SET c2_date_time = localtimestamp WHERE c1_no = 1;=# SELECT * FROM t1logical;
c1_no |
c2_date_time-------+---------------------------1 | 2018-01-05 11:10:14.419084(1 row)

Su b scrip tio n 側のテーブルでもレコードがU PDATE されている事を確認します。(レプリケーションされている事を確認)(node2のサーバにおいて実施)=# SELECT * FROM t1logical;
c1_no |
c2_date_time-------+---------------------------1 | 2018-01-05 11:10:14.419084(1 row)

3. テーブルのデータを削除
Pu b licatio n 側のテーブルのレコードをDELETE します。

(node1のサーバにおいて実施)=# DELETE FROM t1logical WHERE c1_no = 1;=# SELECT * FROM t1logical;
c1_no | c2_date_time-------+-------------(0 rows)<-- 指定のデータが削除される

Su b scrip tio n 側のテーブルでもレコードがDELETE されている事を確認します。(レプリケーションされている事を確認)(node2のサーバにおいて実施)=# SELECT * FROM t1logical;
c1_no | c2_date_time-------+-------------(0 rows)<-- 指定のデータが削除される

4. テーブルロックを実施し同期レプリケーションを確認
Su b scrip tio n 側のデータベース、テーブルのo id を確認します。

Page 139 of 285

PostgreSQL Enterprise Consortium(node2のサーバにおいて実施)=# SELECT oid, datname FROM pg_database;oid |datname-------+--------------24748 | testlogicaldb[省略]=# SELECT oid, datname FROM pg_database;relid |
relname-------+----------------24750 | t1logical[省略]

Su b scrip tio n 側のロック情報を確認します。

(node2のサーバにおいて実施)=# SELECT locktype, database, relation, mode FROM pg_locks;
locktype | database | relation |mode------------+----------+----------+----------------relation |24748 |
11577 | AccessShareLock<--

ロックされていない

virtualxid |(2 rows)|| ExclusiveLock

Su b scrip tio n 側のテーブルをトランザクション内でロック状態のままにします。(ロックモード : EXCLU SIVE MO DE)(node2のサーバにおいて実施)=# BEGIN;=# LOCK t1logical IN ACCESS EXCLUSIVE MODE ;<-- テーブルをロック

Su b scrip tio n 側のロック情報を確認します。

(node2のサーバにおいて実施)=# SELECT locktype, database, relation, mode FROM pg_locks;
locktype| database | relation |mode---------------+----------+----------+--------------------relation|
24748 |
11577 | AccessShareLockvirtualxid||| ExclusiveLocktransactionid ||| ExclusiveLock
relation|
24748 |
24750 | AccessExclusiveLock<-- テーブルをロック[省略]

Su b scrip tio n 側のテーブルがロック状態のまま、Pu b licatio n 側のテーブルにレコードをINSERTします。
Su b scrip tio n 側(スタンバイ)から Co mmit の応答があるまで待機状態となります。
(ただし、60 秒以上応答がない場合は、タイムアウトのログが出力されます。)(node1のサーバにおいて実施)=# INSERT INTO t1logical VALUES (1,localtimestamp);
セカンダリから commit 応答がないためプロンプトが止まる

Page 140 of 285<--

PostgreSQL Enterprise Consortium(node1のサーバにおいて実施)$ tail /var/lib/pgsql/10/data/log/postgresql-2018-01-05.log
LOG: terminating walsender process due to replication timeout
wal_sender_timeout (default 60s)待ってログが出力される<-- 設定値[Ctrl+c]
Cancel request sent
WARNING: canceling wait for synchronous replication due to user request
DETAIL: The transaction has already committed locally, but might not have been
replicated to the standby.
INSERT 0 1
Su b scrip tio n 側のテーブルロックを解除するとレプリケーションが動作します。
Su b scrip tio n 側のテーブルでもレコードがINSERTされている事を確認します。

(node2のサーバにおいて実施)=# COMMIT; <-- ロック解除=# SELECT locktype, database, relation, mode FROM pg_locks;
locktype | database | relation |mode------------+----------+----------+----------------relation |24748 |
11577 | AccessShareLock
ロックされていない

virtualxid ||| ExclusiveLock(2 rows)=# SELECT * FROM t1logical ;c1_no |
c2_date_time-------+---------------------------1 | 2018-01-05 13:44:52.134441<-- レプリケーションされている(1 row)

6.3.3. 複数サ ブスクリ プシ ョ ン
6.3.3.1. 検証目的
ロジカルレプリケーションで複数のSu b scrip tio n にレプリケーションが可能か確認します。

6.3.3.2. 検証内容
本検証では、3 ノードで下記構成のレプリケーションが実現可能かの検証を実施しました。

6.3.3.3. 検証環境

Page 141 of 285<--

PostgreSQL Enterprise Consortium

6.3.3.4. 検証手順
■ ロジカルレプリケーションの動作確認(テーブル単位)
1. 動作確認用のデータベース作成
本検証で利用するデータベースを作成します。

(node1,node2,node3のサーバにおいて実施)$ createdb logicalreptest

以下のコマンドでデータベースが作成された事を確認します。

(node1,node2,node3のサーバにおいて実施)$ psql -U postgres -l
List of databases
Name| Owner | Encoding | Collate | Ctype | Access privileges----------------+----------+----------+---------+-------+----------------------logicalreptest | postgres | UTF8
| C
| C
|
[省略]

2. 動作確認用のテーブル作成
作成したデータベースにテスト用のテーブルを作成します。

(node1,node2,node3のサーバにおいて実施)$ psql -U postgres logicalreptest=# CREATE TABLE t1logical (c1 INT, PRIMARY KEY (c1));=# CREATE TABLE t2logical (c1 INT, PRIMARY KEY (c1));=# CREATE TABLE t3logical (c1 INT, PRIMARY KEY (c1));

以下のコマンドで作成したテーブルを確認します。

Page 142 of 285

PostgreSQL Enterprise Consortium(node1,node2,node3のサーバにおいて実施)=# \dt
List of relations
Schema | Name
| Type | Owner--------+-----------+-------+---------public | t1logical | table | postgres
public | t2logical | table | postgres
public | t3logical | table | postgres(3 rows)

3. 1 セット目Pu b licatio n 、Su b scrip tio n 作成
n o de1 に作成した t1lo gical テーブルをレプリケーション対象とするPu b licatio n を作成します。

(node1のサーバにおいて実施)$ psql -U postgres logicalreptest=# CREATE PUBLICATION pub_node1_t1logical FOR TABLE t1logical;

以下のコマンドでPu b licatio n が作成された事を確認します。

(node1のサーバにおいて実施)=# \dRp
List of publications
Name| Owner | All tables | Inserts | Updates | Deletes---------------------+----------+------------+---------+---------+--------pub_node1_t1logical | postgres | f
| t
| t
| t
(1 row)n o de2 に n o de1 に作成した Pu b licatio n とレプリケーションするSu b scrip tio n を作成します。

(node2のサーバにおいて実施)$ psql -U postgres logicalreptest=# CREATE SUBSCRIPTION sub_node2_t1logical CONNECTION 'host=node1 dbname=logicalreptest
port=5432 user=postgres password=postgres' PUBLICATION pub_node1_t1logical;

以下のコマンドでSu b scrip tio n が作成された事を確認します。

(node2のサーバにおいて実施)=# \dRs
List of subscriptions
Name| Owner | Enabled |Publication---------------------+----------+---------+----------------------sub_node2_t1logical | postgres | t| {pub_node1_t1logical}(1 row)n o de3 に n o de1 に作成した Pu b licatio n とレプリケーションするSu b scrip tio n を作成します。

(node3のサーバにおいて実施)$ psql -U postgres logicalreptest=# CREATE SUBSCRIPTION sub_node3_t1logical CONNECTION 'host=node1 dbname=logicalreptest
port=5432 user=postgres password=postgres' PUBLICATION pub_node1_t1logical;

以下のコマンドでSu b scrip tio n が作成された事を確認します。

Page 143 of 285

PostgreSQL Enterprise Consortium(node3のサーバにおいて実施)=# \dRs
List of subscriptions
Name| Owner | Enabled |Publication---------------------+----------+---------+----------------------sub_node3_t1logical | postgres | t| {pub_node1_t1logical}(1 row)

4. 2 セット目Pu b licatio n 、Su b scrip tio n 作成
n o de2 に作成した t2lo gical テーブルをレプリケーション対象とするPu b licatio n を作成します。

(node2のサーバにおいて実施)$ psql -U postgres logicalreptest=# CREATE PUBLICATION pub_node2_t2logical FOR TABLE t2logical;

以下のコマンドでPu b licatio n が作成された事を確認します。

(node2のサーバにおいて実施)=# \dRp
List of publications
Name| Owner | All tables | Inserts | Updates | Deletes---------------------+----------+------------+---------+---------+--------pub_node2_t2logical | postgres | f
| t
| t
| t
(1 row)n o de3 に n o de2 に作成した Pu b licatio n とレプリケーションするSu b scrip tio n を作成します。

(node3のサーバにおいて実施)$ psql -U postgres logicalreptest=# CREATE SUBSCRIPTION sub_node3_t2logical CONNECTION 'host=node2 dbname=logicalreptest
port=5432 user=postgres password=postgres' PUBLICATION pub_node2_t2logical;

以下のコマンドでSu b scrip tio n が作成された事を確認します。

(node3のサーバにおいて実施)=# \dRs
List of subscriptions
Name| Owner | Enabled |Publication---------------------+----------+---------+----------------------sub_node3_t1logical | postgres | t| {pub_node1_t1logical}
sub_node3_t2logical | postgres | t| {pub_node2_t2logical}(2 rows)

n o de1 に n o de2 に作成した Pu b licatio n とレプリケーションするSu b scrip tio n を作成します。

(node1のサーバにおいて実施)$ psql -U postgres logicalreptest=# CREATE SUBSCRIPTION sub_node1_t2logical CONNECTION 'host=node2 dbname=logicalreptest
port=5432 user=postgres password=postgres' PUBLICATION pub_node2_t2logical;

以下のコマンドでSu b scrip tio n が作成された事を確認します。

Page 144 of 285

PostgreSQL Enterprise Consortium(node1のサーバにおいて実施)=# \dRs
List of subscriptions
Name| Owner | Enabled |Publication---------------------+----------+---------+----------------------sub_node1_t2logical | postgres | t| {pub_node2_t2logical}(1 row)

5. 3 セット目Pu b licatio n 、Su b scrip tio n 作成
n o de3 に作成した t3lo gical テーブルをレプリケーション対象とするPu b licatio n を作成します。

(node3のサーバにおいて実施)$ psql -U postgres logicalreptest=# CREATE PUBLICATION pub_node3_t3logical FOR TABLE t3logical;

以下のコマンドでPu b licatio n が作成された事を確認します。

=# \dRp
List of publications
Name| Owner | All tables | Inserts | Updates | Deletes---------------------+----------+------------+---------+---------+--------pub_node3_t3logical | postgres | f
| t
| t
| t
(1 row)n o de3 に作成した Pu b licatio n とレプリケーションするSu b scrip tio n を作成します。

(node2のサーバにおいて実施)$ psql -U postgres logicalreptest=# CREATE SUBSCRIPTION sub_node2_t3logical CONNECTION 'host=node3 dbname=logicalreptest
port=5432 user=postgres password=postgres' PUBLICATION pub_node3_t3logical;

以下のコマンドでSu b scrip tio n が作成された事を確認します。

=# \dRs
List of subscriptions
Name| Owner | Enabled |Publication---------------------+----------+---------+----------------------sub_node2_t1logical | postgres | t| {pub_node1_t1logical}
sub_node2_t3logical | postgres | t| {pub_node3_t3logical}(2 rows)

n o de3 に作成した Pu b licatio n とレプリケーションするSu b scrip tio n を作成します。

(node1のサーバにおいて実施)$ psql -U postgres logicalreptest=# CREATE SUBSCRIPTION sub_node1_t3logical CONNECTION 'host=node3 dbname=logicalreptest
port=5432 user=postgres password=postgres' PUBLICATION pub_node3_t3logical;

以下のコマンドでSu b scrip tio n が作成された事を確認します。

Page 145 of 285

PostgreSQL Enterprise Consortium(node1のサーバにおいて実施)=# \dRs
List of subscriptions
Name| Owner | Enabled |Publication---------------------+----------+---------+----------------------sub_node1_t2logical | postgres | t| {pub_node2_t2logical}
sub_node1_t3logical | postgres | t| {pub_node3_t3logical}(2 rows)

6. 1 セット目 ロジカルレプリケーション動作検証
n o de1 のPu b licatio n 側のテーブル(t1lo gical)にレコードをINSERTします。

(node1のサーバにおいて実施)$ psql -U postgres logicalreptest=# INSERT INTO t1logical VALUES (1);=# INSERT INTO t1logical VALUES (2);=# SELECT * FROM t1logical;
c1
---1
2
(2 rows)

n o de2 のSu b scrip tio n 側のテーブルにもレコードがINSERTされている事を確認します。(レプリケーションされている事を確認)(node2のサーバにおいて実施)$ psql -U postgres logicalreptest=# SELECT * FROM t1logical;
c1---1<-- node1と同じ結果がレプリケーションされていること
2(2 rows)

n o de3 のSu b scrip tio n 側のテーブルにもレコードがINSERTされている事を確認します。(レプリケーションされている事を確認)(node3のサーバにおいて実施)$ psql -U postgres logicalreptest=# SELECT * FROM t1logical;
c1---1<-- node1と同じ結果がレプリケーションされていること
2(2 rows)

7. 2 セット目 ロジカルレプリケーション動作検証
n o de2 のPu b licatio n 側のテーブル(t2lo gical)にレコードをINSERTします。

Page 146 of 285

PostgreSQL Enterprise Consortium(node2のサーバにおいて実施)$ psql -U postgres logicalreptest=# INSERT INTO t2logical VALUES (3);=# INSERT INTO t2logical VALUES (4);=# SELECT * FROM t2logical;
c1
---3
4
(2 rows)

n o de3 のSu b scrip tio n 側のテーブルにもレコードがINSERTされている事を確認します。(レプリケーションされている事を確認)(node3のサーバにおいて実施)$ psql -U postgres logicalreptest=# SELECT * FROM t2logical;
c1---3<-- node2と同じ結果がレプリケーションされていること
4(2 rows)

n o de1 のSu b scrip tio n 側のテーブルにもレコードがINSERTされている事を確認します。(レプリケーションされている事を確認)(node1のサーバにおいて実施)$ psql -U postgres logicalreptest=# SELECT * FROM t2logical;
c1---3<-- node2と同じ結果がレプリケーションされていること
4(2 rows)

8. 3 セット目 ロジカルレプリケーション動作検証
n o de3 のPu b licatio n 側のテーブル(t3lo gical)にレコードをINSERTします。

(node3のサーバにおいて実施)$ psql -U postgres logicalreptest=# INSERT INTO t3logical VALUES (5);=# INSERT INTO t3logical VALUES (6);=# SELECT * FROM t3logical;
c1
---5
6
(2 rows)

n o de2 のSu b scrip tio n 側のテーブルにもレコードがINSERTされている事を確認します。(レプリケーションされている事を確認)

Page 147 of 285

PostgreSQL Enterprise Consortium(node2のサーバにおいて実施)$ psql -U postgres logicalreptest=# SELECT * FROM t3logical;
c1---5<-- node3同じ結果がレプリケーションされていること
6(2 rows)

n o de1 のSu b scrip tio n 側のテーブルにもレコードがINSERTされている事を確認します。(レプリケーションされている事を確認)(node1のサーバにおいて実施)$ psql -U postgres logicalreptest=# SELECT * FROM t3logical;
c1---5<-- node3と同じ結果がレプリケーションされていること
6(2 rows)■ 動作確認1. VIEW でのステータス確認
n o de1 での各Pu b licatio n /Su b scrip tio n の定義を確認します。

(node1のサーバにおいて実施)$ psql -U postgres logicalreptest
・ Publicationに設定したテーブルを確認=# SELECT * FROM pg_publication_tables;
pubname| schemaname | tablename---------------------+------------+----------pub_node1_t1logical | public| t1logical(1 row)
・レプリケーションしている状態の確認=# SELECT pid, application_name, client_addr, state, sync_state FROM
pg_stat_replication;pid | application_name | client_addr | state | sync_state------+---------------------+---------------+-----------+-----------1291 | sub_node2_t1logical | node2| streaming | async
1298 | sub_node3_t1logical | node3| streaming | async(2 rows)
・ Subscriptionの設定情報=# SELECT subslotname, subpublications, subconninfo FROM pg_subscription;
subslotname|
subpublications|
subconninfo---------------------+-----------------------+---------------------------------------------------------------------------sub_node1_t2logical | {pub_node2_t2logical} | host=node2 dbname=logicalreptest
port=5432 user=postgres password=postgres
sub_node1_t3logical | {pub_node3_t3logical} | host=node3 dbname=logicalreptest
port=5432 user=postgres password=postgres(2 rows)

n o de2 での各Pu b licatio n /Su b scrip tio n の定義を確認します。

Page 148 of 285

PostgreSQL Enterprise Consortium(node2のサーバにおいて実施)$ psql -U postgres logicalreptest
・ Publicationに設定したテーブルを確認=# SELECT * FROM pg_publication_tables;
pubname| schemaname | tablename---------------------+------------+----------pub_node2_t2logical | public| t2logical(1 row)
・レプリケーションしている状態の確認=# SELECT pid, application_name, client_addr, state, sync_state FROM
pg_stat_replication;pid | application_name | client_addr | state | sync_state------+---------------------+---------------+-----------+-----------1323 | sub_node1_t2logical | node1| streaming | async
1344 | sub_node3_t2logical | node3| streaming | async(2 rows)
・ Subscriptionの設定情報=# SELECT subslotname, subpublications, subconninfo FROM pg_subscription;
subslotname|
subpublications|
subconninfo---------------------+-----------------------+---------------------------------------------------------------------------sub_node2_t3logical | {pub_node3_t3logical} | host=node3 dbname=logicalreptest
port=5432 user=postgres password=postgres
sub_node2_t1logical | {pub_node1_t1logical} | host=node1 dbname=logicalreptest
port=5432 user=postgres password=postgres(2 rows)

n o de3 での各Pu b licatio n /Su b scrip tio n の定義を確認します。

Page 149 of 285

PostgreSQL Enterprise Consortium(node3において実施)$ psql -U postgres logicalreptest
・ Publicationに設定したテーブルを確認=# SELECT * FROM pg_publication_tables;
pubname| schemaname | tablename---------------------+------------+----------pub_node3_t3logical | public| t3logical(1 row)
・レプリケーションしている状態の確認=# SELECT pid, application_name, client_addr, state, sync_state FROM
pg_stat_replication;pid | application_name | client_addr | state | sync_state------+---------------------+---------------+-----------+-----------1346 | sub_node1_t3logical | node1| streaming | async
1348 | sub_node2_t3logical | node2| streaming | async(2 rows)
・ Subscriptionの設定情報=# SELECT subslotname, subpublications, subconninfo FROM pg_subscription;
subslotname|
subpublications|
subconninfo---------------------+-----------------------+--------------------------------------------------------------------------sub_node3_t1logical | {pub_node1_t1logical} | host=node1 dbname=logicalreptest
port=5432 user=postgres password=postgres
sub_node3_t2logical | {pub_node2_t2logical} | host=node2 dbname=logicalreptest
port=5432 user=postgres password=postgres(2 rows)

6.3.4. カスケー ド構成
6.3.4.1. 検証目的
ロジカルレプリケーションを利用したカスケード構成の挙動を確認します。

6.3.4.2. 検証内容
本検証では、下記構成のレプリケーションが実現可能かの検証を実施しました。

6.3.4.3. 検証環境

Page 150 of 285

PostgreSQL Enterprise Consortium

6.3.4.4. 検証手順
■ ロジカルレプリケーションの動作確認(テーブル単位)
1. 動作確認用のデータベース作成
本検証で利用するデータベースを作成します。

(node1,node2,node3のサーバにおいて実施)$ createdb logicalreptest

以下のコマンドでデータベースが作成された事を確認します。

(node1,node2,node3のサーバにおいて実施)$ psql -U postgres -l
List of databases
Name| Owner | Encoding | Collate | Ctype | Access privileges----------------+----------+----------+---------+-------+----------------------logicalreptest | postgres | UTF8
| C
| C
|
[省略]

2. 動作確認用のテーブル作成
作成したデータベースにテスト用のテーブルを作成します。

(node1,node2,node3のサーバにおいて実施)$ psql -U postgres logicalreptest=# CREATE TABLE t1logical (c1 INT, PRIMARY KEY (c1));

以下のコマンドで作成したテーブルを確認します。

(node1,node2,node3のサーバにおいて実施)=# \dList of relations
Schema | Name
| Type | Owner--------+-----------+-------+---------public | t1logical | table | postgres(1 row)

3. Pu b licatio n 作成
n o de1 に作成した t1lo gical テーブルをレプリケーション対象とするPu b licatio n を作成します。

(node1のサーバにおいて実施)$ psql -U postgres logicalreptest=# CREATE PUBLICATION pub_node1_t1logical FOR TABLE t1logical;

以下のコマンドでPu b licatio n が作成された事を確認します。

Page 151 of 285

PostgreSQL Enterprise Consortium(node1のサーバにおいて実施)$ psql -U postgres logicalreptest=# \dRp
List of publications
Name| Owner | All tables | Inserts | Updates | Deletes---------------------+----------+------------+---------+---------+--------pub_node1_t1logical | postgres | f
| t
| t
| t
(1 row)

4. Su b scrip tio n 作成
n o de2 に n o de1 に作成した Pu b licatio n とレプリケーションするSu b scrip tio n を作成します。

(node2のサーバにおいて実施)$ psql -U postgres logicalreptest=# CREATE SUBSCRIPTION sub_node2_t1logical CONNECTION 'host=node1 dbname=logicalreptest
port=5432 user=postgres password=postgres' PUBLICATION pub_node1_t1logical;

以下のコマンドでSu b scrip tio n が作成された事を確認します。

(node2のサーバにおいて実施)$ psql -U postgres logicalreptest=# \dRs
List of subscriptions
Name| Owner | Enabled |Publication---------------------+----------+---------+----------------------sub_node2_t1logical | postgres | t| {pub_node1_t1logical}(1 row)

5. Pu b licatio n 作成
n o de1 に作成した Pu b licatio n とレプリケーションする対象となっていたテーブル(t1lo gical)を新たにレプリケーション対象とした Pu b licatio n をn o de2 に作成します。

(node2のサーバにおいて実施)$ psql -U postgres logicalreptest=# CREATE PUBLICATION pub_node2_t1logical FOR TABLE t1logical;

以下のコマンドでPu b licatio n が作成された事を確認します。

(node2のサーバにおいて実施)$ psql -U postgres logicalreptest=# \dRp
List of publications
Name| Owner | All tables | Inserts | Updates | Deletes---------------------+----------+------------+---------+---------+--------pub_node2_t1logical | postgres | f
| t
| t
| t
(1 row)

6. Su b scrip tio n 作成
n o de3 に n o de2 に作成した Pu b licatio n とレプリケーションするSu b scrip tio n を作成します。

Page 152 of 285

PostgreSQL Enterprise Consortium(node3のサーバにおいて実施)$ psql -U postgres logicalreptest=# CREATE SUBSCRIPTION sub_node3_t1logical CONNECTION 'host=node2 dbname=logicalreptest
port=5432 user=postgres password=postgres' PUBLICATION pub_node2_t1logical;

以下のコマンドでSu b scrip tio n が作成された事を確認します。

(node3のサーバにおいて実施)$ psql -U postgres logicalreptest=# \dRs
List of subscriptions
Name| Owner | Enabled |Publication---------------------+----------+---------+----------------------sub_node3_t1logical | postgres | t| {pub_node2_t1logical}(1 row)

7. ロジカルレプリケーション簡易動作検証
n o de1 のPu b licatio n 側のテーブルにレコードをINSERTします。

(node1のサーバにおいて実施)$ psql -U postgres logicalreptest=# INSERT INTO t1logical VALUES (1);=# INSERT INTO t1logical VALUES (2);=# SELECT * FROM t1logical;
c1
---1
2
(2 rows)

8. ロジカルレプリケーション簡易動作検証確認
n o de2 のSu b scrip tio n 側のテーブルにもレコードがINSERTされている事を確認します。(n o de1 からレプリケーションされている事を確認)(node2のサーバにおいて実施)$ psql -U postgres logicalreptest=# SELECT * FROM t1logical;
c1---1<-- node1と同じ結果がレプリケーションされていること
2(2 rows)

n o de3 のSu b scrip tio n 側のテーブルにもレコードがINSERTされている事を確認します。(n o de2 からレプリケーションされている事を確認)(node3のサーバにおいて実施)$ psql -U postgres logicalreptest=# SELECT * FROM t1logical;
c1---1<-- node1と同じ結果がレプリケーションされていること
2(2 rows)■ 動作確認Page 153 of 285

PostgreSQL Enterprise Consortium

1. VIEW でのステータス確認
n o de1 でPu b licatio n の定義を確認します。

(node1のサーバにおいて実施)$ psql -U postgres logicalreptest
・ Publicationに設定したテーブルを確認=# SELECT * FROM pg_publication_tables;
pubname| schemaname | tablename---------------------+------------+----------pub_node1_t1logical | public| t1logical(1 row)
・レプリケーションしている状態の確認=# SELECT pid, application_name, client_addr, state, sync_state FROM
pg_stat_replication;pid | application_name | client_addr | state | sync_state------+---------------------+---------------+-----------+-----------31121| sub_node2_t1logical | node2| streaming | async(1 rows)

n o de2 でのPu b licatio n /Su b scrip tio n の定義を確認します。

(node2のサーバにおいて実施)$ psql -U postgres logicalreptest
・ Publicationに設定したテーブルを確認=# SELECT * FROM pg_publication_tables;
pubname| schemaname | tablename---------------------+------------+----------pub_node2_t1logical | public| t1logical(1 row)
・レプリケーションしている状態の確認=# SELECT pid, application_name, client_addr, state, sync_state FROM
pg_stat_replication;pid | application_name | client_addr | state | sync_state------+---------------------+---------------+-----------+-----------9185 | sub_node3_t1logical | node3| streaming | async(1 rows)
・ Subscriptionの設定情報=# SELECT subslotname, subpublications, subconninfo FROM pg_subscription;
subslotname|
subpublications|
subconninfo---------------------+-----------------------+---------------------------------------------------------------------------sub_node2_t1logical | {pub_node1_t1logical} | host=node1 dbname=logicalreptest
port=5432 user=postgres password=postgres(1 rows)

n o de3 のSu b scrip tio n の定義を確認します。

Page 154 of 285

PostgreSQL Enterprise Consortium(node3において実施)$ psql -U postgres logicalreptest
・レプリケーションしている状態の確認=# SELECT pid, application_name, client_addr, state, sync_state FROM
pg_stat_replication;(0 rows)
・ Subscriptionの設定情報=# SELECT subslotname, subpublications, subconninfo FROM pg_subscription;
subslotname|
subpublications|
subconninfo---------------------+-----------------------+--------------------------------------------------------------------------sub_node3_t1logical | {pub_node2_t1logical} | host=node2 dbname=logicalreptest
port=5432 user=postgres password=postgres(1 row)■ 追加検証(Pu b licatio n とSu b scrip tio n の関係をループさせた場合の挙動)
1. Pu b licatio n 作成
n o de2 に作成した Pu b licatio n とレプリケーションする対象となっていたテーブル(t1lo gical)を新たにレプリケーション対象とした Pu b licatio n をn o de3 に作成します。

(node3のサーバにおいて実施)$ psql -U postgres logicalreptest=# CREATE PUBLICATION pub_node3_t1logical FOR TABLE t1logical;

以下のコマンドでPu b licatio n が作成された事を確認します。

$ psql -U postgres logicalreptest=# \dRp
List of publications
Name| Owner | All tables | Inserts | Updates | Deletes---------------------+----------+------------+---------+---------+--------pub_node3_t1logical | postgres | f
| t
| t
| t
(1 row)

2. Su b scrip tio n 作成
n o de1 に n o de3 に作成した Pu b licatio n とレプリケーションするSu b scrip tio n を作成します。

(node1のサーバにおいて実施)$ psql -U postgres logicalreptest=# CREATE SUBSCRIPTION sub_node1_t1logical CONNECTION 'host=node3 dbname=logicalreptest
port=5432 user=postgres password=postgres' PUBLICATION pub_node3_t1logical;

3. ロジカルレプリケーション簡易動作検証(ループ時)
レプリケーション元とレプリケーション先がループしているため、初期データのコピー時に一意制約違反が発生し、5 秒毎にエラーメッセージが出力されます。

Page 155 of 285

PostgreSQL Enterprise Consortium(node1のサーバにおいて実施)$ psql -U postgres logicalreptest=# INSERT INTO t1logical VALUES (1);(PostgreSQLログのエラーメッセージ)[2018-01-31 17:18:03.060 JST][][][00000][444] LOG: logical replication tablesynchronization worker for subscription "sub_node1_t1logical", table "t1logical" hasstarted[2018-01-31 17:18:03.072 JST][][][23505][444] ERROR: duplicate key value violates
unique constraint "t1logical_pkey"[2018-01-31 17:18:03.072 JST][][][23505][444] DETAIL: Key (c1)=(1) already exists.
[2018-01-31 17:18:03.072 JST][][][23505][444] CONTEXT: COPY t1logical, line 1[2018-01-31 17:18:03.073 JST][][][00000][227] LOG: worker process: logical replication
worker for subscription 16393 sync 16385 (PID 444) exited with exit code 1

6.3.5. パー ティシ ョ ニ ン グとの 組み 合わせ
6.3.5.1. 検証構成

6.3.5.2. 組み 合わせた 場合の利点
パーティショニングは大規模なテーブルに対し、データを複数テーブルに分割して格納する事で性能や運用性を向上させることができます。
また、ロジカルレプリケーションは論理的な変更内容を伝播させてレプリケーションさせる事で、複数サーバからデータを複製先のデータベースに集約させる事ができます。
これらを組み合わせる事で、複数サーバのデータを1 つの集約サーバ上でパーティションテーブルとして管理する事が可能となります。

6.3.5.3. 検証内容
■ レプリケーション対象としたパーティショニングテーブルがSu b scrib er に反映されることを確認する。
■ Pu b lish er と同等の処理がSu b scrib er のパーティショニングテーブルに行われる事を確認する。
■ 複数サーバの子テーブルを1 つのサーバのパーティショニングテーブルに統合する構成が組めるか確認する。
6.3.5.4. 検証結果
■ 環境構築1. レプリケーション状態の確認
現在のレプリケーション状態を確認します。

(node1,node2のサーバにおいて実施)$ psql -x -U postgres -c "SELECT * FROM pg_stat_replication;"(0 rows)

Page 156 of 285

PostgreSQL Enterprise Consortium

2. 試験用データベース作成
本検証で利用するデータベースを作成します。

(node1,node2のサーバにおいて実施)$ createdb testlogicalre

以下のコマンドでデータベースが作成された事を確認します。

(node1,node2のサーバにおいて実施)$ psql -U postgres -l
List of databases
Name| Owner | Encoding | Collate | Ctype | Access privileges---------------+----------+----------+---------+-------+----------------------testlogicalre | postgres | UTF8
| C
| C
|
[省略]

3. 試験用テーブル作成(p gb en ch )作成したデータベースに p gb en ch を使用してテスト用のテーブルを作成します。
その後に、対象のテーブル(p gb en ch _h isto ry)をパーティション構文を用いてパーティショニング化します。

(node1,node2のサーバにおいて実施)$ pgbench -i -U postgres testlogicalre$ psql -U postgres testlogicalre=# DROP TABLE pgbench_history;=# CREATE TABLE pgbench_history(tid integer, bid integer, aid integer, delta integer,
mtime timestamp without time zone, filler character(22)) PARTITION BY RANGE (mtime);=# CREATE TABLE pgbench_history_201801 PARTITION OF pgbench_history FOR VALUES FROM('2018-01-01') TO ('2018-02-01');=# CREATE TABLE pgbench_history_201712 PARTITION OF pgbench_history FOR VALUES FROM('2017-12-01') TO ('2018-01-01');=# CREATE TABLE pgbench_history_201711 PARTITION OF pgbench_history FOR VALUES FROM('2017-11-01') TO ('2017-12-01');=# ALTER TABLE pgbench_history_201801 ADD CONSTRAINT pgbench_history_201801_pkey PRIMARY
KEY(tid, aid);=# ALTER TABLE pgbench_history_201712 ADD CONSTRAINT pgbench_history_201712_pkey PRIMARY
KEY(tid, aid);=# ALTER TABLE pgbench_history_201711 ADD CONSTRAINT pgbench_history_201711_pkey PRIMARY
KEY(tid, aid);=# SELECT relname, n_live_tup AS rowcount FROM pg_stat_all_tables WHERE relname LIKE
'pgbench_history%' ORDER BY relname;relname| rowcount------------------------+---------pgbench_history_201711 |0pgbench_history_201712 |0pgbench_history_201801 |0(3 rows)

4. Pu b licatio n とSu b scrip tio n を作成
n o de1 に作成したパーティションの子テーブルをレプリケーション対象とするPu b licatio n を作成します。

Page 157 of 285

PostgreSQL Enterprise Consortium(node1のサーバにおいて実施)=# CREATE PUBLICATION pub_node1_parttables FOR TABLE pgbench_history_201711;=# ALTER PUBLICATION pub_node1_parttables ADD TABLE pgbench_history_201712;=# ALTER PUBLICATION pub_node1_parttables ADD TABLE pgbench_history_201801;=# SELECT * FROM pg_publication_tables;
pubname| schemaname |tablename----------------------+------------+-----------------------pub_node1_parttables | public| pgbench_history_201801
pub_node1_parttables | public| pgbench_history_201712
pub_node1_parttables | public| pgbench_history_201711(3 rows)

以下のコマンドでPu b licatio n が作成された事を確認します。

(node1のサーバにおいて実施)=# \dRp
List of publications
Name| Owner | All tables | Inserts | Updates | Deletes----------------------+----------+------------+---------+---------+--------pub_node1_parttables | postgres | f
| t
| t
| t
(1 row)n o de2 に n o de1 に作成した Pu b licatio n とレプリケーションするSu b scrip tio n を作成します。

(node2のサーバにおいて実施)=# CREATE SUBSCRIPTION sub_node2_parttables CONNECTION 'host=node1 dbname=testlogicalre
port=5432 user=postgres' PUBLICATION pub_node1_parttables;=# SELECT * FROM pg_subscription_rel;
srsubid | srrelid | srsubstate | srsublsn---------+---------+------------+-----------18381 | 18372 | r| 0/109C2570
18381 | 18369 | r| 0/109C2570
18381 | 18366 | r| 0/109C25A8(3 rows)

以下のコマンドでSu b scrip tio n が作成された事を確認します。

(node2のサーバにおいて実施)=# \dRs
List of subscriptions
Name| Owner | Enabled |Publication----------------------+----------+---------+-----------------------sub_node2_parttables | postgres | t| {pub_node1_parttables}(1 row)■ ロジカルレプリケーション簡易動作確認
1. テーブルデータ追加
p gb en ch で動作させるスクリプトを作成します。

Page 158 of 285

PostgreSQL Enterprise Consortium(node1のサーバにおいて実施)$ vi part_test.sql----\set aid random(1, 90 * :scale)\set bid random(1, 1 * :scale)\set tid random(1, 10000 * :scale)\set delta random(-5000, 5000)
BEGIN;INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (:tid, :bid, :aid,:delta, CAST('2018-01-31' AS date) - CAST(:aid AS integer));
UPDATE pgbench_history SET filler = to_char(:bid + :delta, '9999') WHERE tid = :tid AND
aid = :aid;
END;
-----

n o de1 のPu b licatio n 側のテーブルに対して、p gb en ch でスクリプトを実行します。

(node1のサーバにおいて実施)$ pgbench -U postgres -c 1 -t 1000 -f part_test.sql testlogicalre

レコードがパーティショニングテーブルに振り分けられている事を確認します。

(node1のサーバにおいて実施)$ psql -U postgres testlogicalre=# ANALYZE;=# SELECT relname, n_live_tup AS rowcount FROM pg_stat_all_tables WHERE relname LIKE
'pgbench_history%' ORDER BY relname;relname| rowcount------------------------+---------pgbench_history_201711 |348
pgbench_history_201712 |332pgbench_history_201801 |320(3 rows)

Pu b licatio n 側のパーティショニングテーブルのレコード情報を確認します。

Page 159 of 285

PostgreSQL Enterprise Consortium(node1のサーバにおいて実施)=# SELECT * FROM pgbench_history_201711 ORDER BY tid LIMIT 3;
tid | bid | aid | delta |
mtime|
filler-----+-----+-----+-------+---------------------+-----------------------27 | 1 | 75 | 2772 | 2017-11-17 00:00:00 | 2773
125 | 1 | 90 | 1513 | 2017-11-02 00:00:00 | 1514
146 | 1 | 81 | -3348 | 2017-11-11 00:00:00 | -3347(3 rows)(node1のサーバにおいて実施)=# SELECT * FROM pgbench_history_201712 ORDER BY tid LIMIT 3;
tid | bid | aid | delta |
mtime|
filler-----+-----+-----+-------+---------------------+-----------------------66 | 1 | 35 | -4563 | 2017-12-27 00:00:00 | -4562
156 | 1 | 55 | 1206 | 2017-12-07 00:00:00 | 1207
231 | 1 | 51 | -894 | 2017-12-11 00:00:00 | -893
(3 rows)(node1のサーバにおいて実施)=# SELECT * FROM pgbench_history_201801 ORDER BY tid LIMIT 3;
tid | bid | aid | delta |
mtime|
filler-----+-----+-----+-------+---------------------+-----------------------13 | 1 | 22 | 339 | 2018-01-09 00:00:00 | 340
22 | 1 | 29 | -1540 | 2018-01-02 00:00:00 | -1539
41 | 1 | 13 | -4569 | 2018-01-18 00:00:00 | -4568(3 rows)

パーティショニングテーブルの予測行数がn o de1 と一致しているか否かを確認します。

(node2のサーバにおいて実施)$ psql -U postgres testlogicalre=# ANALYZE;=# SELECT relname, n_live_tup AS rowcount FROM pg_stat_all_tables WHERE relname LIKE
'pgbench_history%' ORDER BY relname;relname| rowcount------------------------+---------pgbench_history_201711 |348
pgbench_history_201712 |332pgbench_history_201801 |320(3 rows)

Su b scrip tio n 側のパーティショニングテーブルでn o de1 とレコードが一致している事を確認します。(レプリケーションされている事を確認)

Page 160 of 285

PostgreSQL Enterprise Consortium(node2のサーバにおいて実施)=# SELECT * FROM pgbench_history_201711 ORDER BY tid LIMIT 3;
tid | bid | aid | delta |
mtime|
filler-----+-----+-----+-------+---------------------+-----------------------27 | 1 | 75 | 2772 | 2017-11-17 00:00:00 | 2773
125 | 1 | 90 | 1513 | 2017-11-02 00:00:00 | 1514
146 | 1 | 81 | -3348 | 2017-11-11 00:00:00 | -3347(3 rows)=# SELECT * FROM pgbench_history_201712 ORDER BY tid LIMIT 3;
tid | bid | aid | delta |
mtime|
filler-----+-----+-----+-------+---------------------+-----------------------66 | 1 | 35 | -4563 | 2017-12-27 00:00:00 | -4562
156 | 1 | 55 | 1206 | 2017-12-07 00:00:00 | 1207
231 | 1 | 51 | -894 | 2017-12-11 00:00:00 | -893
(3 rows)=# SELECT * FROM pgbench_history_201801 ORDER BY tid LIMIT 3;
tid | bid | aid | delta |
mtime|
filler-----+-----+-----+-------+---------------------+-----------------------13 | 1 | 22 | 339 | 2018-01-09 00:00:00 | 340
22 | 1 | 29 | -1540 | 2018-01-02 00:00:00 | -1539
41 | 1 | 13 | -4569 | 2018-01-18 00:00:00 | -4568(3 rows)

2. レプリケーション状態確認
現在のレプリケーション状態を確認します。
作成した Pu b licatio n とSu b scrip tio n が正常にレプリケーションされているかを
ap p licatio n _n ame 、state 、syn c_state 項目で確認します。

(node1のサーバにおいて実施)$ psql -x -U postgres -c "SELECT * FROM pg_stat_replication;"-[ RECORD 1 ]----+-----------------------------pid| 1446
usesysid| 10
usename| postgres
application_name | sub_node2_parttables<-- subscriptionの名前が表示される
client_addr| 192.168.56.102
client_hostname |client_port| 52428
backend_start| 2018-02-05 10:21:33.710471+09
backend_xmin|
state| streaming<-- streaming が表示される
sent_lsn| 0/10A29160
write_lsn| 0/10A29160
flush_lsn| 0/10A29160
replay_lsn| 0/10A29160
write_lag|
flush_lag|
replay_lag|
sync_priority| 0
sync_state| async<-- async(非同期)■ テーブル追加
1. テーブル作成
新たにパーティショニングテーブルを作成します。

Page 161 of 285

PostgreSQL Enterprise Consortium(node1,node2のサーバにおいて実施)$ psql -U postgres testlogicalre=# CREATE TABLE pgbench_history_201802 PARTITION OF pgbench_history FOR VALUES FROM('2018-02-01') TO ('2018-03-01');=# ALTER TABLE pgbench_history_201802 ADD CONSTRAINT pgbench_history_201802_pkey PRIMARY
KEY(tid, aid);

以下のコマンドで作成したテーブルを確認します。

=# \dt
List of relations
Schema |Name| Type | Owner--------+------------------------+-------+---------public | pgbench_accounts| table | postgres
public | pgbench_branches| table | postgres
public | pgbench_history| table | postgres
public | pgbench_history_201711 | table | postgres
public | pgbench_history_201712 | table | postgres
public | pgbench_history_201801 | table | postgres
public | pgbench_history_201802 | table | postgres
public | pgbench_tellers| table | postgres(8 rows)

2. 既存のロジカルレプリケーションにテーブルを追加作成したパーティショニングテーブルを既存のPu b licatio n に追加します。

(node1のサーバにおいて実施)$ psql -U postgres testlogicalre=# ALTER PUBLICATION pub_node1_parttables ADD TABLE pgbench_history_201802;=# SELECT * FROM pg_publication_tables;
pubname| schemaname |tablename----------------------+------------+-----------------------pub_node1_parttables | public| pgbench_history_201801
pub_node1_parttables | public| pgbench_history_201712
pub_node1_parttables | public| pgbench_history_201711
pub_node1_parttables | public| pgbench_history_201802(4 rows)更新した Pu b licatio n の情報をSu b scrip tio n に反映させます。

Page 162 of 285

PostgreSQL Enterprise Consortium(node2のサーバにおいて実施)$ psql -U postgres testlogicalre=# SELECT * FROM pg_subscription_rel;
srsubid | srrelid | srsubstate | srsublsn---------+---------+------------+-----------18381 | 18372 | r| 0/109C2570
18381 | 18369 | r| 0/109C2570
18381 | 18366 | r| 0/109C25A8(3 rows)=# ALTER SUBSCRIPTION sub_node2_parttables REFRESH PUBLICATION;=# SELECT * FROM pg_subscription_rel;
srsubid | srrelid | srsubstate | srsublsn---------+---------+------------+-----------18381 | 18382 | r| 0/10A3EB98
18381 | 18372 | r| 0/109C2570
18381 | 18369 | r| 0/109C2570
18381 | 18366 | r| 0/109C25A8(4 rows)■ ロジカルレプリケーション動作確認
1. 新規テーブルにデータ追加
新たに作成した Pu b licatio n 側のパーティショニングテーブルにレコードをINSERTします。

(node1のサーバにおいて実施)$ psql -U postgres testlogicalre=# SELECT * FROM pgbench_history_201802;
tid | bid | aid | delta | mtime | filler-----+-----+-----+-------+-------+-------(0 rows)=# INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (7777, 1, 12, 4321,'2018-02-11 00:00:00');=# SELECT * FROM pgbench_history_201802;
tid | bid | aid | delta |mtime| filler------+-----+-----+-------+---------------------+-------7777 | 1 | 12 | 4321 | 2018-02-11 00:00:00 |(1 row)

Su b scrip tio n 側のパーティショニングテーブルにもレコードがINSERTされている事を確認します。(レプリケーションされている事を確認)(node2のサーバにおいて実施)$ psql -U postgres testlogicalre=# SELECT * FROM pgbench_history_201802;
tid | bid | aid | delta |mtime| filler------+-----+-----+-------+---------------------+-------7777 | 1 | 12 | 4321 | 2018-02-11 00:00:00 |(1 row)■ 子テーブルの取り外し/取り付け
1. パーティションからテーブルを取り外し
n o de1 のパーティショニングテーブルの取り外しとPu b licatio n からの削除を行います。

Page 163 of 285

PostgreSQL Enterprise Consortium(node1のサーバにおいて実施)$ psql -U postgres testlogicalre=# SELECT * FROM pgbench_history WHERE tid = 7777;
tid | bid | aid | delta |mtime| filler------+-----+-----+-------+---------------------+-------7777 | 1 | 24 | 4321 | 2018-02-11 00:00:00 |(1 row)=# SELECT * FROM pg_publication_tables;
pubname| schemaname |tablename----------------------+------------+-----------------------pub_node1_parttables | public| pgbench_history_201801
pub_node1_parttables | public| pgbench_history_201712
pub_node1_parttables | public| pgbench_history_201711
pub_node1_parttables | public| pgbench_history_201802(4 rows)=# ALTER TABLE pgbench_history DETACH PARTITION pgbench_history_201802;=# ALTER PUBLICATION pub_node1_parttables DROP TABLE pgbench_history_201802;=# SELECT * FROM pgbench_history WHERE tid = 7777;
tid | bid | aid | delta | mtime | filler-----+-----+-----+-------+-------+-------(0 rows)=# SELECT * FROM pg_publication_tables;
pubname| schemaname |tablename----------------------+------------+-----------------------pub_node1_parttables | public| pgbench_history_201801
pub_node1_parttables | public| pgbench_history_201712
pub_node1_parttables | public| pgbench_history_201711(3 rows)

n o de2 のパーティショニングテーブルの取り外しと更新した Pu b licatio n の情報のSu b scrip tio n への反映を行います。

Page 164 of 285

PostgreSQL Enterprise Consortium(node2のサーバにおいて実施)$ psql -U postgres testlogicalre=# SELECT * FROM pgbench_history WHERE tid = 7777;
tid | bid | aid | delta |mtime| filler------+-----+-----+-------+---------------------+-------7777 | 1 | 24 | 4321 | 2018-02-11 00:00:00 |(1 row)=# SELECT * FROM pg_subscription_rel;
srsubid | srrelid | srsubstate | srsublsn---------+---------+------------+-----------18381 | 18382 | r| 0/10A3EB98
18381 | 18372 | r| 0/109C2570
18381 | 18369 | r| 0/109C2570
18381 | 18366 | r| 0/109C25A8(4 rows)=# ALTER TABLE pgbench_history DETACH PARTITION pgbench_history_201802;=# ALTER SUBSCRIPTION sub_node2_parttables REFRESH PUBLICATION;=# SELECT * FROM pgbench_history WHERE tid = 7777;
tid | bid | aid | delta | mtime | filler-----+-----+-----+-------+-------+-------(0 rows)=# SELECT * FROM pg_subscription_rel;
srsubid | srrelid | srsubstate | srsublsn---------+---------+------------+-----------18381 | 18372 | r| 0/109C2570
18381 | 18369 | r| 0/109C2570
18381 | 18366 | r| 0/109C25A8(3 rows)

2. パーティションにテーブルを取り付け
n o de1 のパーティショニングテーブルの取り付けとPu b licatio n への追加を行います。

(node1のサーバにおいて実施)$ psql -U postgres testlogicalre=# TRUNCATE TABLE pgbench_history_201802;=# ALTER TABLE pgbench_history ATTACH PARTITION pgbench_history_201802 FOR VALUES FROM('2018-02-01') TO ('2018-03-01');=# ALTER PUBLICATION pub_node1_parttables ADD TABLE pgbench_history_201802;=# SELECT * FROM pg_publication_tables;
pubname| schemaname |tablename----------------------+------------+-----------------------pub_node1_parttables | public| pgbench_history_201801
pub_node1_parttables | public| pgbench_history_201712
pub_node1_parttables | public| pgbench_history_201711
pub_node1_parttables | public| pgbench_history_201802(4 rows)

n o de2 のパーティショニングテーブルの取り付けと更新した Pu b licatio n の情報のSu b scrip tio n への反映を行います。

Page 165 of 285

PostgreSQL Enterprise Consortium(node2のサーバにおいて実施)$ psql -U postgres testlogicalre=# TRUNCATE TABLE pgbench_history_201802;=# ALTER TABLE pgbench_history ATTACH PARTITION pgbench_history_201802 FOR VALUES FROM('2018-02-01') TO ('2018-03-01');=# ALTER SUBSCRIPTION sub_node2_parttables REFRESH PUBLICATION;=# SELECT * FROM pg_subscription_rel;
srsubid | srrelid | srsubstate | srsublsn---------+---------+------------+-----------18381 | 18382 | r| 0/10A523F8
18381 | 18372 | r| 0/109C2570
18381 | 18369 | r| 0/109C2570
18381 | 18366 | r| 0/109C25A8(4 rows)■ 子テーブルをレプリケーションし、パーティショニングを構成

1. 試験用データベース作成
本検証で利用するデータベースを作成します。

(node1のサーバにおいて実施)$ createdb testlogicalre2(node2のサーバにおいて実施)$ createdb testlogicalre2_c1$ createdb testlogicalre2_c2$ createdb testlogicalre2_c3

以下のコマンドでデータベースが作成された事を確認します。

Page 166 of 285

PostgreSQL Enterprise Consortium(node1のサーバにおいて実施)$ psql -U postgres -p 5432 -l
List of databases
Name| Owner | Encoding | Collate | Ctype | Access privileges----------------+----------+----------+---------+-------+----------------------testlogicalre2 | postgres | UTF8
| C
| C
|
[省略]
(node2のサーバにおいて実施)$ psql -U postgres -p 5432 -l
List of databases
Name| Owner | Encoding | Collate | Ctype | Access privileges-------------------+----------+----------+---------+-------+----------------------testlogicalre2_c1 | postgres | UTF8
| C
| C
|
testlogicalre2_c2 | postgres | UTF8
| C
| C
|
testlogicalre2_c3 | postgres | UTF8
| C
| C
|
[省略]

2. 試験用テーブル作成(p gb en ch )n o de1 で作成したデータベースに p gb en ch を使用してテスト用のテーブルを作成します。
その後に、対象のテーブル(p gb en ch _h isto ry)をパーティション構文を用いてパーティショニング化します。

(node1のサーバにおいて実施)$ pgbench -i -U postgres testlogicalre2$ psql -U postgres testlogicalre2=# DROP TABLE pgbench_history;=# CREATE TABLE pgbench_history(tid integer, bid integer, aid integer, delta integer,
mtime timestamp without time zone, filler character(22)) PARTITION BY RANGE (mtime);=# CREATE TABLE pgbench_history_201711 PARTITION OF pgbench_history FOR VALUES FROM('2017-11-01') TO ('2017-12-01');=# CREATE TABLE pgbench_history_201712 PARTITION OF pgbench_history FOR VALUES FROM('2017-12-01') TO ('2018-01-01');=# CREATE TABLE pgbench_history_201801 PARTITION OF pgbench_history FOR VALUES FROM('2018-01-01') TO ('2018-02-01');=# ALTER TABLE pgbench_history_201711 ADD CONSTRAINT pgbench_history_201711_pkey PRIMARY
KEY(tid, aid);=# ALTER TABLE pgbench_history_201712 ADD CONSTRAINT pgbench_history_201712_pkey PRIMARY
KEY(tid, aid);=# ALTER TABLE pgbench_history_201801 ADD CONSTRAINT pgbench_history_201801_pkey PRIMARY
KEY(tid, aid);=# SELECT relname, n_live_tup AS rowcount FROM pg_stat_all_tables WHERE relname LIKE
'pgbench_history%' ORDER BY relname;relname| rowcount------------------------+---------pgbench_history_201711 |0pgbench_history_201712 |0pgbench_history_201801 |0(3 rows)

n o de2 で作成した各データベースにテーブルを作成します。

Page 167 of 285

PostgreSQL Enterprise Consortium(node2のサーバにおいて実施)$ psql -U postgres -p 5432 testlogicalre2_c1=# CREATE TABLE pgbench_history_201711(tid integer, bid integer, aid integer, delta
integer, mtime timestamp without time zone, filler character(22));=# ALTER TABLE pgbench_history_201711 ADD CONSTRAINT pgbench_history_201711_pkey PRIMARY
KEY(tid, aid);$ psql -U postgres -p 5432 testlogicalre2_c2=# CREATE TABLE pgbench_history_201712(tid integer, bid integer, aid integer, delta
integer, mtime timestamp without time zone, filler character(22));=# ALTER TABLE pgbench_history_201712 ADD CONSTRAINT pgbench_history_201712_pkey PRIMARY
KEY(tid, aid);$ psql -U postgres -p 5432 testlogicalre2_c3=# CREATE TABLE pgbench_history_201801(tid integer, bid integer, aid integer, delta
integer, mtime timestamp without time zone, filler character(22));=# ALTER TABLE pgbench_history_201801 ADD CONSTRAINT pgbench_history_201801_pkey PRIMARY
KEY(tid, aid);

3. Pu b licatio n とSu b scrip tio n を作成
n o de2 の各データベースに作成したテーブルをレプリケーション対象とするPu b licatio n をそれぞれ作成します。

(node2のサーバにおいて実施)$ psql -U postgres -p 5432 testlogicalre2_c1=# CREATE PUBLICATION pub_node2_parttables1 FOR TABLE pgbench_history_201711;$ psql -U postgres -p 5432 testlogicalre2_c2=# CREATE PUBLICATION pub_node2_parttables2 FOR TABLE pgbench_history_201712;$ psql -U postgres -p 5432 testlogicalre2_c3=# CREATE PUBLICATION pub_node2_parttables3 FOR TABLE pgbench_history_201801;n o de1 に n o de2 に作成した Pu b licatio n とレプリケーションするSu b scrip tio n を作成します。

(node1のサーバにおいて実施)$ psql -U postgres -p 5432 testlogicalre2=# CREATE SUBSCRIPTION sub_node1_parttables1 CONNECTION 'host=node2
dbname=testlogicalre2_c1 port=5432 user=postgres' PUBLICATION pub_node2_parttables1;=# CREATE SUBSCRIPTION sub_node1_parttables2 CONNECTION 'host=node2
dbname=testlogicalre2_c2 port=5432 user=postgres' PUBLICATION pub_node2_parttables2;=# CREATE SUBSCRIPTION sub_node1_parttables3 CONNECTION 'host=node2
dbname=testlogicalre2_c3 port=5432 user=postgres' PUBLICATION pub_node2_parttables3;

4. ロジカルレプリケーション簡易動作確認
n o de2 の各データベースのPu b licatio n 側のテーブルにレコードをINSERTします。

Page 168 of 285

PostgreSQL Enterprise Consortium(node2のサーバにおいて実施)$ psql -U postgres -p 5432 testlogicalre2_c1=# INSERT INTO pgbench_history_201711 (tid, bid, aid, delta, mtime) VALUES (1111, 1, 12,4321, '2017-11-11 00:00:00');=# SELECT * FROM pgbench_history_201711;
tid | bid | aid | delta |mtime| filler------+-----+-----+-------+---------------------+-------1111 | 1 | 12 | 4321 | 2017-11-11 00:00:00 |(1 row)$ psql -U postgres -p 5432 testlogicalre2_c2=# INSERT INTO pgbench_history_201712 (tid, bid, aid, delta, mtime) VALUES (2222, 2, 34,8765, '2017-12-11 00:00:00');=# SELECT * FROM pgbench_history_201712;
tid | bid | aid | delta |mtime| filler------+-----+-----+-------+---------------------+-------2222 | 2 | 34 | 8765 | 2017-12-11 00:00:00 |(1 row)$ psql -U postgres -p 5432 testlogicalre2_c3=# INSERT INTO pgbench_history_201801 (tid, bid, aid, delta, mtime) VALUES (3333, 3, 56,
2109, '2018-01-11 00:00:00');=# SELECT * FROM pgbench_history_201801;tid | bid | aid | delta |mtime| filler------+-----+-----+-------+---------------------+-------3333 | 3 | 56 | 2109 | 2018-01-11 00:00:00 |(1 row)

Su b scrip tio n 側のテーブルにもレコードがINSERTされている事を確認します。(レプリケーションされている事を確認)(node1のサーバにおいて実施)$ psql -U postgres -p 5432 testlogicalre2=# SELECT * FROM pgbench_history;
tid | bid | aid | delta |mtime| filler------+-----+-----+-------+---------------------+-------3333 | 3 | 56 | 2109 | 2018-01-11 00:00:00 |2222 | 2 | 34 | 8765 | 2017-12-11 00:00:00 |1111 | 1 | 12 | 4321 | 2017-11-11 00:00:00 |(3 rows)

6.4. ロジカルレプリケーションの運用
6.4.1. 監視
ロジカルレプリケーションが稼働する環境を監視する際に利用する情報について説明します。

6.4.1.1. 前提と する環境
本章は以下の環境を利用した検証結果を元に解説します。なお、ストリーミングレプリケーションと共用する情報(動的統計情報ビュー p g_stat_rep licatio n 等)もあるた
め、それぞれの表示形式の違いが比較できるようにロジカルレプリケーションとストリーミングレプリケーションを両方利用する環境を利用します。

Page 169 of 285

PostgreSQL Enterprise Consortium

Po stgreSQ L サーバは3 台構成
ロジカルレプリケーションは、Pu b lish er1 台（サーバ1 。IP アドレス 192 ｡168 ｡127 ｡31 ）、Su b scrib er1 台（サーバ2 。IP アドレス 192 ｡168 ｡127 ｡32 ）で構成
ストリーミングレプリケーションは、マスタ 1 台（ Pu b lish er と同一）、スタンバイ1 台（サーバ3 。IP アドレス 192 ｡168 ｡127 ｡33 ）で構成
ロジカルレプリケーションのPu b lish er では、データベースクラスタ内に 2 つのデータベースを作成
p u b db : レプリケーション対象のテーブルを個別定義するPu b licatio n を作成
p u b db _all_tab le: データベース内に存在する全テーブルをレプリケーション対象とするPu b licatio n を作成
ロジカルレプリケーションのSu b scrib er では、データベースクラスタ内に 2 つのデータベースを作成
su b db : Pu b lish er のp u b db データベースに定義した Pu b licatio n から変更データを受け取るSu b scrip tio n を作成
su b db _all_tab le: Pu b lish er のp u b db _all_tab le データベースに定義した Pu b licatio n から変更データを受け取るSu b scrip tio n を作成
6.4.1.2. ロ ジカ ルレプリケ ーショ ンの設定
6.4.1.2.1. Publisher で確 認 でき る 情 報

Pu b lish er のサーバにおいて、Pu b licatio n が作成されたデータベースに接続して確認できる情報は以下のとおりです。
■ システムカ タ ロ グ pg_publication [1]接続中のデータベースに定義されているPu b licatio n の情報を確認できます。 Pu b licatio n の名前、所有者に加えて、レプリケーションが行われる更新処理の種別がわかります。 また、FO R ALL TAB LES 句を指定して作成した Pu b licatio n は、p u b alltab les = t となります。

(サーバ1において実施)
pubdb=# select oid, * from pg_publication;oid | pubname | pubowner | puballtables | pubinsert | pubupdate | pubdelete-------+---------+----------+--------------+-----------+-----------+----------16392 | pub1|
16386 | f
| t
| t
| t
16399 | pub2|
16386 | f
| t
| t
| t
16471 | pub3|
10 | f| t| t| t
pubdb_all_table=# select oid, * from pg_publication;oid |pubname| pubowner | puballtables | pubinsert | pubupdate | pubdelete-------+---------------+----------+--------------+-----------+-----------+----------16475 | pub_all_table |10 | t| t| t| t[1] Po stgreSQ L 10.0 文書 - 51.40. p g_p u b licatio n■ システムカ タ ロ グ pg_publication_rel [2]接続中のデータベースに定義されているPu b licatio n がレプリケーション対象とするテーブルを確認できます。

Page 170 of 285

PostgreSQL Enterprise Consortium(サーバ1において実施)
pubdb=# select * from pg_publication_rel;
prpubid | prrelid---------+--------16392 | 16387
16399 | 16394
16471 | 16465
16392 | 16496
pubdb_all_table=# select * from pg_publication_rel;
prpubid | prrelid---------+--------ただし、Pu b licatio n 、テーブルのO ID しか保有しないため、実際に利用するには p g_p u b licatio n , p g_class と結合して、 それぞれの名称を取得する必要があるでしょう。また、FO R ALL TAB LES 句を指定して作成した Pu b licatio n では レプリケーション対象となっているテーブルが表示されません。

[2] Po stgreSQ L 10.0 文書 - 51.41. p g_p u b licatio n _rel
■ システムカ タ ロ グ pg_publication_tables [3]接続中のデータベースに定義されているPu b licatio n がレプリケーション対象とするテーブルを確認できます。

(サーバ1において実施)
pubdb=# select * from pg_publication_tables;
pubname | schemaname | tablename---------+------------+----------pub1| public| data1
pub1| public| data1_1
pub2| public| data2
pub3| public| data3
pubdb_all_table=# select * from pg_publication_tables;
pubname| schemaname | tablename---------------+------------+----------pub_all_table | public| data1
pub_all_table | public| data2
前述のp g_p u b licatio n _rel と比較して、Pu b licatio n 、テーブルの名前が直接確認できること、 FO R ALL TAB LES 句を指定して作成した Pu b licatio n でもレプリケー
ション対象のテーブルが確認できることから 実際の運用ではこちらを活用するとよいでしょう。

[3] Po stgreSQ L 10.0 文書 - 51.78. p g_p u b licatio n _tab les■psqlのメ タ コ マンド
p sql コマンドのメタコマンド「 dRp + 」でもPu b licatio n の情報を確認できます。

Page 171 of 285

PostgreSQL Enterprise Consortium(サーバ1において実施)
pubdb=# \dRp+
Publication pub1
Owner | All tables | Inserts | Updates | Deletes---------+------------+---------+---------+--------pubusr1 | f
| t
| t
| t
Tables:"public.data1""public.data1_1"
Publication pub2
Owner | All tables | Inserts | Updates | Deletes---------+------------+---------+---------+--------pubusr1 | f
| t
| t
| t
Tables:"public.data2"
Publication pub3
Owner | All tables | Inserts | Updates | Deletes----------+------------+---------+---------+--------postgres | f
| t
| t
| t
Tables:"public.data3"
pubdb_all_table=# \dRp+
Publication pub_all_table
Owner | All tables | Inserts | Updates | Deletes----------+------------+---------+---------+--------postgres | t
| t
| t
| t
システムカタログp g_p u b licatio n , p g_p u b licatio n _rel の情報がまとめて表示されますが、 FO R ALL TAB LES 句を指定して作成した Pu b licatio n のテーブルは表示されません。

■ システムカ タ ロ グ pg_replication_slots [4]Pu b lish er に自動作成されるロジカルレプリケーションスロットの情報を確認できます。

(サーバ1において実施)
pubdb=# select * from pg_replication_slots;
slot_name | plugin | slot_type | datoid |database| temporary | active | active_pid| xmin | catalog_xmin | restart_lsn | confirmed_flush_lsn---------------+----------+-----------+--------+-----------------+-----------+--------+-----------+------+--------------+-------------+--------------------sub3| pgoutput | logical | 16384 | pubdb
| f
| t
|
31511
|
|
684 | 0/56083970 | 0/560839A8
sub2| pgoutput | logical | 16384 | pubdb
| f
| t
|
31509
|
|
684 | 0/56083970 | 0/560839A8
sub1| pgoutput | logical | 16384 | pubdb
| f
| t
|
32366
|
|
684 | 0/56083970 | 0/560839A8
sub_all_table | pgoutput | logical | 16474 | pubdb_all_table | f
| t
|
31512
|
|
684 | 0/56083970 | 0/560839A8
Pu b licatio n の情報は接続するデータベースに存在するものしか見えませんが、レプリケーションスロットはデータベースクラスタ内に存在するものが全て表示されます。 なお、
slo t_typ e はロジカルレプリケーションスロットでは lo gical 、ストリーミングレプリケーションスロットは p h ysical と表示されます。
[4] Po stgreSQ L 10.0 文書 - 51.80. p g_rep licatio n _slo ts
6.4.1.2.2. Subscriber で確 認 でき る 情 報

Su b scrib er のサーバにおいて、Su b scrip tio n が作成されたデータベースに接続して確認できる情報は以下のとおりです。
■ システムカ タ ロ グ pg_subscription [5]データベースに存在するSu b scrip tio n の情報を確認できます。 Su b scrip tio n の名前、所有者に加えて、接続先のデータベース、Pu b licatio n の名前等がわかります。
なお、p g_p u b licatio n では接続したデータベース内のPu b licatio n のみが見えましたが、p g_su b scrip tio n では接続したデータベースに関わらず、 全てのSu b scrip tio n が表示されます。

Page 172 of 285

PostgreSQL Enterprise Consortium(サーバ2において実施)
subdb=# select * from pg_subscription;
subdbid |subname| subowner | subenabled |subconninfo| subslotname | subsynccommit | subpublications---------+---------------+----------+------------+-------------------------------------------------------------------------+---------------+---------------+----------------16384 | sub2|
10 | t| host=192.168.127.31 dbname=pubdb user=repusr1
password=repusr1| sub2| off| {pub2}
16384 | sub3|
10 | t| host=192.168.127.31 dbname=pubdb user=repusr1
password=repusr1| sub3| off| {pub3}
16477 | sub_all_table |10 | t| host=192.168.127.31 dbname=pubdb_all_tableuser=repusr1 password=repusr1 | sub_all_table | off| {pub_all_table}
16384 | sub1|
10 | t| host=192.168.127.31 dbname=pubdb user=repusr1
password=repusr1| sub1| off| {pub1}[5] Po stgreSQ L 10.0 文書 - 51.52. p g_su b scrip tio n■ システムカ タ ロ グ pg_subscription_rel [6]接続中のデータベースに定義されているSu b scrip tio n がレプリケーション対象とするテーブルを確認できます。 また、対象テーブルの他に、レプリケーションのステータス(i =in itialize, d = data is b ein g co p ied, s = syn ch ro n ized, r = ready (n o rmal rep licatio n ))やLSN が表示されます。

(サーバ2において実施)
subdb=# select * from pg_subscription_rel;
srsubid | srrelid | srsubstate | srsublsn---------+---------+------------+-----------16463 | 16455 | r| 0/55FB63D0
16400 | 16395 | r| 0/16D7B60
16476 | 16470 | r| 0/55FDE4A0
16463 | 16490 | r| 0/56068D38
subdb_all_table=# select * from pg_subscription_rel;
srsubid | srrelid | srsubstate | srsublsn---------+---------+------------+-----------16484 | 16479 | r| 0/5602B818
16484 | 16485 | r| 0/56048B00
ただし、Su b scrip tio n 、テーブルのO ID しか保有しないため、実際に利用するには p g_su b scrip tio n , p g_class と結合して、それぞれの名称を取得する必要があるでしょう。

[6] Po stgreSQ L 10.0 文書 - 51.53. p g_su b scrip tio n _rel
■psqlのメ タ コ マンド
p sql コマンドのメタコマンド「 dRs+ 」でもSu b scrip tio n の情報を確認できます。 システムカタログp g_su b scrip tio n と同様の情報が見やすく整形されて表示されます。

Page 173 of 285

PostgreSQL Enterprise Consortium(サーバ2において実施)
subdb=# \dRs+
List of subscriptions
Name | Owner | Enabled | Publication | Synchronous commit |Conninfo------+----------+---------+-------------+--------------------+--------------------------------------------------------------sub1 | postgres | t| {pub1}| off| host=192.168.127.31 dbname=pubdb
user=repusr1 password=repusr1
sub2 | postgres | t| {pub2}| off| host=192.168.127.31 dbname=pubdb
user=repusr1 password=repusr1
sub3 | postgres | t| {pub3}| off| host=192.168.127.31 dbname=pubdb
user=repusr1 password=repusr1
subdb_all_table=# \dRs+
List of subscriptions
Name| Owner | Enabled | Publication | Synchronous commit |Conninfo---------------+----------+---------+-----------------+--------------------+------------------------------------------------------------------------sub_all_table | postgres | t| {pub_all_table} | off| host=192.168.127.31
dbname=pubdb_all_table user=repusr1 password=repusr1
なお、p g_su b scrip tio n ではデータベースクラスタ内のSU B SCRIPTIO N が全て表示されたのに対し、メタコマンド「 dRs+ 」では接続中のデータベース内のSu b scrip tio n
しか表示されません。

6.4.1.3. ロ ジカ ルレプリケ ーショ ンの稼働状態
6.4.1.3.1. Publisher で確 認 でき る 情 報

Pu b lish er のサーバにおいて確認できる情報は以下のとおりです。
■ サーバロ グ
CREATE PU B LICATIO N 文を実行した時点ではPu b lish er のサーバログには何も表示されません。 Su b scrib er でCREATE SU B SCRIPTIO N 文を実行した時点で以下のログが出力されます。

(サーバ1のサーバログ)
2018-02-06 11:24:40.990 JST [20086] LOG: starting logical decoding for slot "sub_all_table"
2018-02-06 11:24:40.990 JST [20086] DETAIL: streaming transactions committing after 0/56084A90,
reading WAL from 0/56084A58■ 動的統計情報ビ ュー pg_stat_replication [7]ストリーミングレプリケーションと同様に、ロジカルレプリケーションの稼働状況を確認できます。 接続中のデータベースに関わらず、データベースクラスタ全体の情報が表示されます。

(サーバ1において実施)
postgres=# \xpostgres=# select * from pg_stat_replication;-[ RECORD 1 ]----+-----------------------------pid| 20086
usesysid| 16385
usename| repusr1
application_name | sub_all_tableclient_addr| 192.168.127.32
client_hostname |client_port| 46452
backend_start| 2018-02-06 11:24:40.988512+09
backend_xmin|
state| streaming
sent_lsn| 0/58000140
write_lsn| 0/58000140
flush_lsn| 0/58000140
replay_lsn| 0/58000140
write_lag|
flush_lag|
Page 174 of 285

PostgreSQL Enterprise Consortium

replay_lag|
sync_priority| 0
sync_state| async-[ RECORD 2 ]----+-----------------------------pid| 31511
usesysid| 16385
usename| repusr1
application_name | sub3
client_addr| 192.168.127.32
client_hostname |client_port| 46353
backend_start| 2018-01-17 11:07:07.204636+09
backend_xmin|
state| streaming
sent_lsn| 0/58000140
write_lsn| 0/58000140
flush_lsn| 0/58000140
replay_lsn| 0/58000140
write_lag|
flush_lag|
replay_lag|
sync_priority| 0
sync_state| async-[ RECORD 3 ]----+-----------------------------pid| 32366
usesysid| 16385
usename| repusr1
application_name | sub1
client_addr| 192.168.127.32
client_hostname |client_port| 46411backend_start| 2018-01-17 15:01:47.434102+09
backend_xmin|
state| streaming
sent_lsn| 0/58000140
write_lsn| 0/58000140
flush_lsn| 0/58000140
replay_lsn| 0/58000140
write_lag|
flush_lag|
replay_lag|
sync_priority| 0
sync_state| async-[ RECORD 4 ]----+-----------------------------pid| 31509
usesysid| 16385
usename| repusr1
application_name | sub2
client_addr| 192.168.127.32
client_hostname |client_port| 46351
backend_start| 2018-01-17 11:07:07.188398+09
backend_xmin|
state| streaming
sent_lsn| 0/58000140
write_lsn| 0/58000140
flush_lsn| 0/58000140
replay_lsn| 0/58000140
write_lag|
flush_lag|
replay_lag|
sync_priority| 0
sync_state| async-[ RECORD 5 ]----+-----------------------------pid| 21625
usesysid| 16385
usename| repusr1
Page 175 of 285

PostgreSQL Enterprise Consortium

application_name | nk_PGECons3
client_addr| 192.168.127.33
client_hostname |client_port| 55519
backend_start| 2018-02-06 16:00:40.749454+09
backend_xmin|
state| streaming
sent_lsn| 0/58000140
write_lsn| 0/58000140
flush_lsn| 0/58000140
replay_lsn| 0/58000140
write_lag|
flush_lag|
replay_lag|
sync_priority| 0
sync_state| async
ここでは RECO RD 1 〜4 でサーバ1-2 間のロジカルレプリケーション、RECO RD 5 でサーバ1-3 間のストリーミングレプリケーションの情報が表示されています。ロジカルレプリ
ケーションとストリーミングレプリケーションはほぼ同じ形式で見えますが、ap p licatio n _n ame はそれぞれ以下の情報が表示されています。
ロジカルレプリケーション： Su b scrip tio n の名前
ストリーミングレプリケーション： スレーブのreco very.co n f に記載した ap p licatio n _n ame
なお、Su b scrip tio n を停止させると、p g_stat_rep licatio n から該当するレプリケージョンの情報が表示されなくなります。

(サーバ2において実施)
subdb=# ALTER SUBSCRIPTION sub1 DISABLE;
ALTER SUBSCRIPTION
pubdb=# select * from pg_stat_replication;-[ RECORD 1 ]----+-----------------------------pid| 20086
usesysid| 16385
usename| repusr1
application_name | sub_all_tableclient_addr| 192.168.127.32
client_hostname |client_port| 46452
backend_start| 2018-02-06 11:24:40.988512+09
backend_xmin|
state| streaming
sent_lsn| 0/580074E0
write_lsn| 0/580074E0
flush_lsn| 0/580074E0
replay_lsn| 0/580074E0
write_lag|
flush_lag|
replay_lag|
sync_priority| 0
sync_state| async-[ RECORD 2 ]----+-----------------------------pid| 31511
usesysid| 16385
usename| repusr1
application_name | sub3
client_addr| 192.168.127.32
client_hostname |client_port| 46353
backend_start| 2018-01-17 11:07:07.204636+09
backend_xmin|
state| streaming
sent_lsn| 0/580074E0
write_lsn| 0/580074E0
flush_lsn| 0/580074E0
replay_lsn| 0/580074E0
write_lag|
flush_lag|
replay_lag|
Page 176 of 285

PostgreSQL Enterprise Consortium

sync_priority| 0
sync_state| async-[ RECORD 3 ]----+-----------------------------pid| 31509
usesysid| 16385
usename| repusr1
application_name | sub2
client_addr| 192.168.127.32
client_hostname |client_port| 46351
backend_start| 2018-01-17 11:07:07.188398+09
backend_xmin|
state| streaming
sent_lsn| 0/580074E0
write_lsn| 0/580074E0
flush_lsn| 0/580074E0
replay_lsn| 0/580074E0
write_lag|
flush_lag|
replay_lag|
sync_priority| 0
sync_state| async-[ RECORD 4 ]----+-----------------------------pid| 21625
usesysid| 16385
usename| repusr1
application_name | nk_PGECons3
client_addr| 192.168.127.33
client_hostname |client_port| 55519
backend_start| 2018-02-06 16:00:40.749454+09
backend_xmin|
state| streaming
sent_lsn| 0/580074E0
write_lsn| 0/580074E0
flush_lsn| 0/580074E0
replay_lsn| 0/580074E0
write_lag|
flush_lag|
replay_lag|
sync_priority| 0
sync_state| async
ロジカルレプリケーションもストリーミングレプリケーションも、p g_stat_rep licatio n の件数を定期的に確認することで稼働状況を監視するとよいでしょう。

[7] Po stgreSQ L 10.0 文書 - 表28.5 p g_stat_rep licatio n ビュー
6.4.1.3.2. Subscriber で確 認 でき る 情 報

Su b scrib er のサーバにおいて確認できる情報は以下のとおりです。
■ サーバロ グ
CREATE SU B SCRIPTIO N 文を実行した時点で以下のログが出力されます。

(サーバ2のサーバログ)
2018-02-06 11:20:29.883 JST [4245] LOG: logical replication apply worker for subscription"sub_all_table" has started
2018-02-06 11:20:29.888 JST [4246] LOG: logical replication table synchronization worker for
subscription "sub_all_table", table "data1" has started
2018-02-06 11:20:29.899 JST [4247] LOG: logical replication table synchronization worker for
subscription "sub_all_table", table "data2" has started
2018-02-06 11:20:29.901 JST [4246] LOG: logical replication table synchronization worker for
subscription "sub_all_table", table "data1" has finished2018-02-06 11:20:29.912 JST [4247] LOG: logical replication table synchronization worker for
subscription "sub_all_table", table "data2" has finished

Page 177 of 285

PostgreSQL Enterprise Consortium「 lo gical rep licatio n tab le syn ch ro n izatio n w o rker fo r su b scrip tio n (Su b scrip tio n 名), tab le (テーブル名) h as fin ish ed 」と出力されていれば、レプリケー
ション対象のテーブルに対する初期データの転送が完了しており正常にロジカルレプリケーションが開始したことが確認できます。

■ 動的統計情報ビ ュー pg_stat_subscription [8]Su b scrip tio n 単位にロジカルレプリケーションの稼働状況を確認できます。 接続中のデータベースに関わらず、データベースクラスタ全体の情報が表示されます。

(サーバ2において実施)
subdb_all_table=# select * from pg_stat_subscription;subid |
subname| pid | relid | received_lsn |last_msg_send_time|
last_msg_receipt_time| latest_end_lsn |latest_end_time-------+---------------+-------+-------+--------------+-------------------------------+------------------------------+----------------+------------------------------16400 | sub2
| 31707 || 0/58000140 | 2018-02-06 16:00:07.649126+09 | 2018-02-06
16:00:07.482495+09 | 0/58000140| 2018-02-06 16:00:07.649126+09
16476 | sub3
| 31709 || 0/58000140 | 2018-02-06 16:00:07.649515+09 | 2018-02-06
16:00:07.482694+09 | 0/58000140| 2018-02-06 16:00:07.649515+09
16463 | sub1
| 32626 || 0/58000140 | 2018-02-06 16:00:07.649203+09 | 2018-02-06
16:00:07.482386+09 | 0/58000140| 2018-02-06 16:00:07.649203+09
16508 | sub_all_table | 4267 || 0/58000140 | 2018-02-06 16:00:07.649259+09 | 2018-02-06
16:00:07.482608+09 | 0/58000140| 2018-02-06 16:00:07.649259+09[8] Po stgreSQ L 10.0 文書 - 表28.7 p g_stat_su b scrip tio n View

6.4.2. 障害時の 動作確認
ロジカルレプリケーションが稼働する環境で障害が発生した場合の挙動を確認します。

6.4.2.1. 前提と する環境
本章は以下の環境を利用した検証結果を元に解説します。

Page 178 of 285

PostgreSQL Enterprise Consortium

ロジカルレプリケーションは、Pu b lish er1 台、Su b scrib er1 台で構成
ロジカルレプリケーションのPu b lish er （サーバ1 。IP アドレス 192 ｡168 ｡127 ｡31 ）では、データベースクラスタ内に 1 つのデータベースを作成
p u b db : レプリケーション対象のテーブルを個別定義するPu b licatio n p u b 1 を作成
ロジカルレプリケーションのSu b scrib er （サーバ2 。IP アドレス 192 ｡168 ｡127 ｡32 ）では、データベースクラスタ内に 1 つのデータベースを作成
su b db : Pu b lish er のp u b db データベースに定義した Pu b licatio n から変更データを受け取るSu b scrip tio n su b 1 を作成

6.4.2.2. 障害ケ ース 1 ロ ジカ ルレプリケ ーショ ン関連プロ セスの異常終了
ロジカルレプリケーションに関連する以下のプロセスが異常終了した時の挙動を確認します。

lo gical rep licatio n lau n ch er
w al sen derlo gical rep licatio n w o rker
■ 初期状態
Pu b lish er では lo gical rep licatio n lau n ch er と w al sen der のプロセスが起動しています。 また、p g_stat_rep licatio n でロジカルレプリケーションが稼働中であることが確認できます。

Page 179 of 285

PostgreSQL Enterprise Consortium(サーバ1において実施)-bash-4.2$ ps aux | grep postgres
postgres 2179 0.0 0.8 389356 16552 ?
postgres 2180 0.0 0.1 242164 2404 ?
postgres 2182 0.0 0.3 389496 5716 ?
postgres 2183 0.0 0.2 389356 4092 ?
postgres 2184 0.0 0.3 389356 6744 ?
postgres 2185 0.0 0.1 389812 3352 ?
process
postgres 2186 0.0 0.1 244420 2620 ?
postgres 2187 0.0 0.1 389648 3060 ?
replication launcher
postgres 21882 0.0 0.3 392540 5780 ?
repusr1 192.168.127.32(46474) idle

S
Ss
Ss
Ss
Ss
Ss

1月09
1月09
1月09
1月09
1月09
1月09

0:56 /usr/pgsql-10/bin/postgres
0:00 postgres: logger process
0:01 postgres: checkpointer process
0:20 postgres: writer process
0:23 postgres: wal writer process
0:38 postgres: autovacuum launcher

Ss
Ss

1月09
1月09

1:09 postgres: stats collector process
0:01 postgres: bgworker: logical

Ss

2月06

0:00 postgres: wal sender process

pubdb=# select * from pg_stat_replication;-[ RECORD 1 ]----+-----------------------------pid| 21882
usesysid| 16385
usename| repusr1
application_name | sub1
client_addr| 192.168.127.32
client_hostname |client_port| 46474
backend_start| 2018-02-06 16:40:08.541313+09
backend_xmin|
state| streaming
sent_lsn| 0/5800DBB0
write_lsn| 0/5800DBB0
flush_lsn| 0/5800DBB0
replay_lsn| 0/5800DBB0
write_lag|
flush_lag|
replay_lag|
sync_priority| 0
sync_state| async
また、Su b scrib er では lo gical rep licatio n lau n ch er と lo gical rep licatio n w o rker のプロセスが起動しており、 p g_stat_su b scrip tio n からもロジカルレプリケー
ションが稼働中であることが確認できます。

Page 180 of 285

PostgreSQL Enterprise Consortium(サーバ2において実施)-bash-4.2$ ps aux | grep postgres
postgres 5405 0.0 0.3 397072 6264 ?
replication worker for subscription 16463
postgres 31698 0.0 0.8 389296 16496 ?
postgres 31699 0.0 0.1 242108 2008 ?
postgres 31701 0.0 0.2 389448 4724 ?
postgres 31702 0.0 0.1 389296 3436 ?
postgres 31703 0.0 0.3 389296 6332 ?
postgres 31704 0.0 0.1 389752 3132 ?
process
postgres 31705 0.0 0.1 244360 2264 ?
postgres 31706 0.0 0.1 389588 2760 ?
replication launcher

Ss

2月06

0:02 postgres: bgworker: logical

S
Ss
Ss
Ss
Ss
Ss

1月17
1月17
1月17
1月17
1月17
1月17

0:47 /usr/pgsql-10/bin/postgres
0:00 postgres: logger process
0:00 postgres: checkpointer process
0:15 postgres: writer process
0:18 postgres: wal writer process
0:32 postgres: autovacuum launcher

Ss
Ss

1月17
1月17

1:07 postgres: stats collector process
0:01 postgres: bgworker: logicalsubdb=# select * from pg_stat_subscription;-[ RECORD 1 ]---------+-----------------------------subid| 16463
subname| sub1pid| 5405
relid|
received_lsn| 0/5800DBB0
last_msg_send_time| 2018-02-07 13:45:31.222443+09
last_msg_receipt_time | 2018-02-07 13:45:31.05565+09
latest_end_lsn| 0/5800DBB0
latest_end_time| 2018-02-07 13:45:31.222443+09■logical replication launcherの停止
Pu b lish er のlo gical rep licatio n lau n ch er をKill コマンドで強制終了すると再度別のプロセスID で起動されていることがわかります。

(サーバ1において実施)-bash-4.2$ kill -9 2187
-bash-4.2$ ps aux | grep postgres
postgres 2179 0.0 0.8 389356 16552 ?
postgres 2180 0.0 0.1 242168 2404 ?
postgres 28990 0.0 0.1 389356 2152 ?
postgres 28991 0.0 0.1 389356 2156 ?
postgres 28992 0.0 0.1 389356 2116 ?
postgres 28993 0.0 0.1 389784 3004 ?
process
postgres 28994 0.0 0.1 244288 1988 ?
postgres 28995 0.0 0.1 389656 2520 ?
replication launcher

S
Ss
Ss
Ss
Ss
Ss

1月09
1月09
13:56
13:56
13:56
13:56

0:56 /usr/pgsql-10/bin/postgres
0:00 postgres: logger process
0:00 postgres: checkpointer process
0:00 postgres: writer process
0:00 postgres: wal writer process
0:00 postgres: autovacuum launcher

Ss
Ss

13:56
13:56

0:00 postgres: stats collector process
0:00 postgres: bgworker: logical

この時Pu b lish er 、Su b scrib er のログにはそれぞれ以下のメッセージが出力されています。

Page 181 of 285

PostgreSQL Enterprise Consortium(サーバ1のサーバログ)
2018-02-07 13:56:05.609 JST [2179] LOG: worker process: logical replication launcher (PID 2187)
was terminated by signal 9: Killed
2018-02-07 13:56:05.609 JST [2179] LOG: terminating any other active server processes
2018-02-07 13:56:05.609 JST [21882] WARNING: terminating connection because of crash of anotherserver process
2018-02-07 13:56:05.609 JST [21882] DETAIL: The postmaster has commanded this server process toroll back the current transaction and exit, because another server process exited abnormally andpossibly corrupted shared memory.
2018-02-07 13:56:05.609 JST [21882] HINT: In a moment you should be able to reconnect to the
database and repeat your command.
2018-02-07 13:56:05.614 JST [2185] WARNING: terminating connection because of crash of anotherserver process
2018-02-07 13:56:05.614 JST [2185] DETAIL: The postmaster has commanded this server process toroll back the current transaction and exit, because another server process exited abnormally andpossibly corrupted shared memory.
2018-02-07 13:56:05.614 JST [2185] HINT: In a moment you should be able to reconnect to the
database and repeat your command.
2018-02-07 13:56:05.620 JST [28988] FATAL: the database system is in recovery mode
2018-02-07 13:56:05.621 JST [2179] LOG: all server processes terminated; reinitializing
2018-02-07 13:56:05.652 JST [28989] LOG: database system was interrupted; last known up at 201802-07 13:45:23 JST
2018-02-07 13:56:05.692 JST [28989] LOG: database system was not properly shut down; automatic
recovery in progress
2018-02-07 13:56:05.693 JST [28989] LOG: redo starts at 0/5800DAD0
2018-02-07 13:56:05.693 JST [28989] LOG: invalid record length at 0/5800DBB0: wanted 24, got 0
2018-02-07 13:56:05.693 JST [28989] LOG: redo done at 0/5800DB78
2018-02-07 13:56:05.698 JST [2179] LOG: database system is ready to accept connections
2018-02-07 13:56:10.640 JST [28998] LOG: starting logical decoding for slot "sub1"
2018-02-07 13:56:10.640 JST [28998] DETAIL: streaming transactions committing after 0/5800DB78,
reading WAL from 0/5800DB78
2018-02-07 13:56:10.640 JST [28998] LOG: logical decoding found consistent point at 0/5800DB78
2018-02-07 13:56:10.640 JST [28998] DETAIL: There are no running transactions.
(サーバ2のサーバログ)
WARNING: terminating connection because of crash of another server process
DETAIL: The postmaster has commanded this server process to roll back the current transaction andexit, because another server process exited abnormally and possibly corrupted shared memory.
HINT: In a moment you should be able to reconnect to the database and repeat your command.
2018-02-07 13:56:05.444 JST [5405] ERROR: could not receive data from WAL stream: server closed
the connection unexpectedlyThis probably means the server terminated abnormally
before or while processing the request.
2018-02-07 13:56:05.446 JST [31698] LOG: worker process: logical replication worker for
subscription 16463 (PID 5405) exited with exit code 1
2018-02-07 13:56:05.450 JST [9938] LOG: logical replication apply worker for subscription "sub1"
has started
2018-02-07 13:56:05.453 JST [9938] ERROR: could not connect to the publisher: FATAL: the databasesystem is in recovery mode
2018-02-07 13:56:05.454 JST [31698] LOG: worker process: logical replication worker for
subscription 16463 (PID 9938) exited with exit code 1
2018-02-07 13:56:10.463 JST [9939] LOG: logical replication apply worker for subscription "sub1"
has started
上記より、Pu b lish er のlo gical rep licatio n lau n ch er が停止した場合、Pu b lish er のインスタンス再起動、リカバリ処理が行われた後にレプリケーションが再開されることがわかりました。
同様に、Su b scrib er のlo gical rep licatio n lau n ch er をKill コマンドで強制終了した場合も、再度別のプロセスID で起動されていることがわかります。また、この時
Su b scrib er のインスタンス再起動、リカバリ処理が行われてからレプリケーションが再開されています。

Page 182 of 285

PostgreSQL Enterprise Consortium(サーバ2において実施)-bash-4.2$ kill -9 31706-bash-4.2$ ps aux | grep postgres
postgres 9947 0.0 0.1 389296 2124 ?
postgres 9948 0.0 0.1 389296 2128 ?
postgres 9949 0.0 0.1 389296 2108 ?
postgres 9950 0.0 0.1 389616 2980 ?
process
postgres 9951 0.0 0.1 244228 2008 ?
postgres 9952 0.0 0.1 389588 2760 ?
replication launcher
postgres 9953 0.0 0.3 397084 6240 ?
replication worker for subscription 16463
postgres 31698 0.0 0.8 389296 16500 ?
postgres 31699 0.0 0.1 242108 2008 ?

Ss
Rs
Rs
Ss

13:57
13:57
13:57
13:57

0:00 postgres: checkpointer process
0:00 postgres: writer process
0:00 postgres: wal writer process
0:00 postgres: autovacuum launcher

Ss
Ss

13:57
13:57

0:00 postgres: stats collector process
0:00 postgres: bgworker: logical

Ss

13:57

0:00 postgres: bgworker: logical

S
Ss

1月17
1月17

0:47 /usr/pgsql-10/bin/postgres
0:00 postgres: logger process(サーバ1のサーバログ)
2018-02-07 13:57:43.391 JST [28998] LOG: unexpected EOF on standby connection
2018-02-07 13:57:43.498 JST [29004] LOG: starting logical decoding for slot "sub1"
2018-02-07 13:57:43.498 JST [29004] DETAIL: streaming transactions committing after 0/5800DC58,
reading WAL from 0/5800DC20
2018-02-07 13:57:43.498 JST [29004] LOG: logical decoding found consistent point at 0/5800DC20
2018-02-07 13:57:43.498 JST [29004] DETAIL: There are no running transactions.
(サーバ2のサーバログ)
2018-02-07 13:57:43.216 JST [31698] LOG: worker process: logical replication launcher (PID 31706)
was terminated by signal 9: Killed
2018-02-07 13:57:43.216 JST [31698] LOG: terminating any other active server processes
2018-02-07 13:57:43.218 JST [31704] WARNING: terminating connection because of crash of anotherserver process
2018-02-07 13:57:43.218 JST [31704] DETAIL: The postmaster has commanded this server process toroll back the current transaction and exit, because another server process exited abnormally andpossibly corrupted shared memory.
2018-02-07 13:57:43.218 JST [31704] HINT: In a moment you should be able to reconnect to the
database and repeat your command.
2018-02-07 13:57:43.226 JST [31698] LOG: all server processes terminated; reinitializing
2018-02-07 13:57:43.250 JST [9946] LOG: database system was interrupted; last known up at 2018-0207 13:46:06 JST
2018-02-07 13:57:43.309 JST [9946] LOG: recovered replication state of node 1 to 0/5800DA98
2018-02-07 13:57:43.309 JST [9946] LOG: database system was not properly shut down; automatic
recovery in progress
2018-02-07 13:57:43.310 JST [9946] LOG: redo starts at 0/73706380
2018-02-07 13:57:43.310 JST [9946] LOG: invalid record length at 0/73706460: wanted 24, got 0
2018-02-07 13:57:43.310 JST [9946] LOG: redo done at 0/73706428
2018-02-07 13:57:43.315 JST [31698] LOG: database system is ready to accept connections
2018-02-07 13:57:43.327 JST [9953] LOG: logical replication apply worker for subscription "sub1"
has started■wal senderの停止
Pu b lish er のw al sen der をKill コマンドで強制終了します。

Page 183 of 285

PostgreSQL Enterprise Consortium(サーバ1において実施)-bash-4.2$ kill -9 29004-bash-4.2$ ps aux | grep postgres
postgres 2179 0.0 0.8 389356 16552 ?
postgres 2180 0.0 0.1 242168 2404 ?
postgres 29085 0.0 0.1 389356 2156 ?
postgres 29086 0.0 0.1 389356 2160 ?
postgres 29087 0.0 0.1 389356 2120 ?
postgres 29088 0.0 0.1 389784 3004 ?
process
postgres 29089 0.0 0.1 244288 1992 ?
postgres 29090 0.0 0.1 389656 2520 ?
replication launcher
postgres 29093 0.0 0.3 392476 5952 ?
repusr1 192.168.127.32(46482) idle

S
Ss
Ss
Ss
Ss
Ss

1月09
1月09
14:12
14:12
14:12
14:12

0:56 /usr/pgsql-10/bin/postgres
0:00 postgres: logger process
0:00 postgres: checkpointer process
0:00 postgres: writer process
0:00 postgres: wal writer process
0:00 postgres: autovacuum launcher

Ss
Ss

14:12
14:12

0:00 postgres: stats collector process
0:00 postgres: bgworker: logical

Ss

14:12

0:00 postgres: wal sender process(サーバ1のサーバログ)
2018-02-07 14:12:19.446 JST [2179] LOG: server process (PID 29004) was terminated by signal 9:
Killed2018-02-07 14:12:19.447 JST [2179] LOG: terminating any other active server processes
2018-02-07 14:12:19.450 JST [28993] WARNING: terminating connection because of crash of anotherserver process
2018-02-07 14:12:19.450 JST [28993] DETAIL: The postmaster has commanded this server process toroll back the current transaction and exit, because another server process exited abnormally andpossibly corrupted shared memory.
2018-02-07 14:12:19.450 JST [28993] HINT: In a moment you should be able to reconnect to the
database and repeat your command.
2018-02-07 14:12:19.456 JST [2179] LOG: all server processes terminated; reinitializing
2018-02-07 14:12:19.485 JST [29083] LOG: database system was interrupted; last known up at 201802-07 14:11:06 JST
2018-02-07 14:12:19.511 JST [29084] FATAL: the database system is in recovery mode
2018-02-07 14:12:19.519 JST [29083] LOG: database system was not properly shut down; automatic
recovery in progress
2018-02-07 14:12:19.519 JST [29083] LOG: redo starts at 0/5800DEE8
2018-02-07 14:12:19.519 JST [29083] LOG: invalid record length at 0/5800DFC8: wanted 24, got 0
2018-02-07 14:12:19.519 JST [29083] LOG: redo done at 0/5800DF90
2018-02-07 14:12:19.524 JST [2179] LOG: database system is ready to accept connections
2018-02-07 14:12:24.529 JST [29093] LOG: starting logical decoding for slot "sub1"
2018-02-07 14:12:24.529 JST [29093] DETAIL: streaming transactions committing after 0/5800DF90,
reading WAL from 0/5800DF90
2018-02-07 14:12:24.529 JST [29093] LOG: logical decoding found consistent point at 0/5800DF90
2018-02-07 14:12:24.529 JST [29093] DETAIL: There are no running transactions.
(サーバ2のサーバログ)
2018-02-07 14:12:19.280 JST [9953] ERROR: could not receive data from WAL stream: server closed
the connection unexpectedlyThis probably means the server terminated abnormally
before or while processing the request.
2018-02-07 14:12:19.281 JST [31698] LOG: worker process: logical replication worker for
subscription 16463 (PID 9953) exited with exit code 1
2018-02-07 14:12:19.285 JST [9998] LOG: logical replication apply worker for subscription "sub1"
has started
2018-02-07 14:12:19.344 JST [9998] ERROR: could not connect to the publisher: FATAL: the databasesystem is in recovery mode
2018-02-07 14:12:19.345 JST [31698] LOG: worker process: logical replication worker for
subscription 16463 (PID 9998) exited with exit code 1
2018-02-07 14:12:24.354 JST [9999] LOG: logical replication apply worker for subscription "sub1"
has started
上記よりPu b lish er のw al sen der が停止した場合も、Pu b lish er インスタンスの再起動、リカバリ処理が行われた後にレプリケーションが再開されることがわかりました。

■logical replication workerの停止
Su b scrib er のlo gical rep licatio n w o rker をKill コマンドで強制終了します。

Page 184 of 285

PostgreSQL Enterprise Consortium(サーバ2において実施)-bash-4.2$ kill -9 9999
-bash-4.2$ ps aux | grep postgres
postgres 10013 0.0 0.1 389296 2124 ?
postgres 10014 0.0 0.1 389296 2128 ?
postgres 10015 0.0 0.1 389296 2104 ?
postgres 10016 0.0 0.1 389616 2980 ?
process
postgres 10017 0.0 0.1 244228 1968 ?
postgres 10018 0.0 0.1 389588 2760 ?
replication launcher
postgres 10019 0.0 0.3 397052 6340 ?
replication worker for subscription 16463
postgres 31698 0.0 0.8 389296 16500 ?
postgres 31699 0.0 0.1 242108 2008 ?

Ss
Ss
Ss
Ss

14:16
14:16
14:16
14:16

0:00 postgres: checkpointer process
0:00 postgres: writer process
0:00 postgres: wal writer process
0:00 postgres: autovacuum launcher

Ss
Ss

14:16
14:16

0:00 postgres: stats collector process
0:00 postgres: bgworker: logical

Ss

14:16

0:00 postgres: bgworker: logical

S
Ss

1月17
1月17

0:47 /usr/pgsql-10/bin/postgres
0:00 postgres: logger process(サーバ1のサーバログ)
2018-02-07 14:16:13.087 JST [29093] LOG: unexpected EOF on standby connection
2018-02-07 14:16:13.161 JST [29139] LOG: starting logical decoding for slot "sub1"
2018-02-07 14:16:13.161 JST [29139] DETAIL: streaming transactions committing after 0/5800E360,
reading WAL from 0/5800E328
2018-02-07 14:16:13.161 JST [29139] LOG: logical decoding found consistent point at 0/5800E328
2018-02-07 14:16:13.161 JST [29139] DETAIL: Logical decoding will begin using saved snapshot.
(サーバ2のサーバログ)
2018-02-07 14:16:12.920 JST [31698] LOG: worker process: logical replication worker for
subscription 16463 (PID 9999) was terminated by signal 9: Killed
2018-02-07 14:16:12.920 JST [31698] LOG: terminating any other active server processes
2018-02-07 14:16:12.924 JST [9950] WARNING: terminating connection because of crash of anotherserver process
2018-02-07 14:16:12.924 JST [9950] DETAIL: The postmaster has commanded this server process toroll back the current transaction and exit, because another server process exited abnormally andpossibly corrupted shared memory.
2018-02-07 14:16:12.924 JST [9950] HINT: In a moment you should be able to reconnect to the
database and repeat your command.
2018-02-07 14:16:12.930 JST [31698] LOG: all server processes terminated; reinitializing
2018-02-07 14:16:12.957 JST [10012] LOG: database system was interrupted; last known up at 201802-07 14:12:43 JST
2018-02-07 14:16:12.973 JST [10012] LOG: recovered replication state of node 1 to 0/5800DEB0
2018-02-07 14:16:12.973 JST [10012] LOG: database system was not properly shut down; automatic
recovery in progress
2018-02-07 14:16:12.974 JST [10012] LOG: redo starts at 0/73706710
2018-02-07 14:16:12.974 JST [10012] LOG: invalid record length at 0/73706A30: wanted 24, got 0
2018-02-07 14:16:12.974 JST [10012] LOG: redo done at 0/737069F8
2018-02-07 14:16:12.974 JST [10012] LOG: last completed transaction was at log time 2018-02-07
14:15:21.893165+09
2018-02-07 14:16:12.980 JST [31698] LOG: database system is ready to accept connections
2018-02-07 14:16:12.990 JST [10019] LOG: logical replication apply worker for subscription "sub1"
has started
上記より、Su b scrib er のlo gical rep licatio n lau n ch er が停止した場合もSu b scrib er インスタンスの再起動、リカバリ処理が行われた後にレプリケーションが再開される
ことがわかりました。

6.4.2.3. 障害ケ ース 2 Publisherのノード 停止
ロジカルレプリケーションのPu b lish er が停止した時の挙動を確認します。

■ 初期状態Su b scrib er のp g_stat_su b scrip tio n よりレプリケーションが稼働中であることを確認しました。

Page 185 of 285

PostgreSQL Enterprise Consortium(サーバ2において実施)
subdb=# select * from pg_stat_subscription;-[ RECORD 1 ]---------+-----------------------------subid| 16463
subname| sub1pid| 10084
relid|
received_lsn| 0/5800ECB8
last_msg_send_time| 2018-02-07 14:37:30.310381+09
last_msg_receipt_time | 2018-02-07 14:37:30.143584+09
latest_end_lsn| 0/5800ECB8
latest_end_time| 2018-02-07 14:37:30.310381+09■Publisherのノード を停止
Pu b lish er で起動中のPo stgreSQ L サーバを強制終了します。

(サーバ1において実施)-bash-4.2$ pg_ctl -m f stop
サーバ停止処理の完了を待っています....完了
サーバは停止しました
次に Su b scrib er のp g_stat_su b scrip tio n を確認すると、レコードは消えないもののp id, LSN 等の情報が表示されなくなりました。

(サーバ2において実施)
subdb=# select * from pg_stat_subscription;-[ RECORD 1 ]---------+-----subid| 16463
subname| sub1
pid
|
relid
|
received_lsn|
last_msg_send_time|
last_msg_receipt_time |latest_end_lsn|
latest_end_time|
また、この時のSu b scrib er のサーバログを確認すると、5 秒毎に接続を試行して lo gical rep licatio n w o rker プロセスがエラー終了する事象が繰り返されていました。

(サーバ2のサーバログ)
2018-02-07 14:55:44.067 JST [10084] LOG: data stream from publisher has ended
2018-02-07 14:55:44.067 JST [10084] ERROR: could not send end-of-streaming message to primary: no
COPY in progress
2018-02-07 14:55:44.069 JST [10075] LOG: worker process: logical replication worker for
subscription 16463 (PID 10084) exited with exit code 1
2018-02-07 14:55:44.072 JST [10118] LOG: logical replication apply worker for subscription "sub1"
has started
2018-02-07 14:55:44.073 JST [10118] ERROR: could not connect to the publisher: FATAL: thedatabase system is shutting down
2018-02-07 14:55:44.074 JST [10075] LOG: worker process: logical replication worker for
subscription 16463 (PID 10118) exited with exit code 1
2018-02-07 14:55:49.083 JST [10119] LOG: logical replication apply worker for subscription "sub1"
has started
2018-02-07 14:55:49.084 JST [10119] ERROR: could not connect to the publisher: could not connect
to server: Connection refused
Is the server running on host "192.168.127.31" and accepting
TCP/IP connections on port 5432?
2018-02-07 14:55:49.085 JST [10075] LOG: worker process: logical replication worker for
subscription 16463 (PID 10119) exited with exit code 1
この状態から、Pu b lish er のPo stgreSQ L サーバを再度起動します。

Page 186 of 285

PostgreSQL Enterprise Consortium(サーバ1において実施)-bash-4.2$ pg_ctl start
サーバの起動完了を待っています....2018-02-07 14:58:44.785 JST [29300] LOG: listening on IPv4 address"0.0.0.0", port 5432
2018-02-07 14:58:44.785 JST [29300] LOG: listening on IPv6 address "::", port 5432
2018-02-07 14:58:44.785 JST [29300] LOG: listening on Unix socket"/var/run/postgresql/.s.PGSQL.5432"
2018-02-07 14:58:44.790 JST [29300] LOG: listening on Unix socket "/tmp/.s.PGSQL.5432"
2018-02-07 14:58:44.807 JST [29300] LOG: redirecting log output to logging collector process
2018-02-07 14:58:44.807 JST [29300] HINT: Future log output will appear in directory "log".
完了
サーバ起動完了
この時、Su b scrib er のp g_stat_su b scrip tio n を見ると初期状態の様にレプリケーションが稼働中であることが確認できました。

(サーバ2において実施)
subdb=# select * from pg_stat_subscription;-[ RECORD 1 ]---------+-----------------------------subid| 16463
subname| sub1pid| 10161
relid|
received_lsn| 0/5800ED60
last_msg_send_time| 2018-02-07 14:58:59.837546+09
last_msg_receipt_time | 2018-02-07 14:58:59.670736+09
latest_end_lsn| 0/5800ED60
latest_end_time| 2018-02-07 14:58:59.837546+09
この時Pu b lish er 、Su b scrib er のログにはそれぞれ以下のメッセージが出力されています。

(サーバ2のサーバログ)
2018-02-07 14:58:49.397 JST [10161] LOG: logical replication apply worker for subscription "sub1"
has started(サーバ1のサーバログ)
2018-02-07 14:55:44.215 JST [2179] LOG: received fast shutdown request
2018-02-07 14:55:44.226 JST [2179] LOG: aborting any active transactions
2018-02-07 14:55:44.232 JST [2179] LOG: worker process: logical replication launcher (PID 29090)
exited with exit code 1
2018-02-07 14:55:44.232 JST [29085] LOG: shutting down
2018-02-07 14:55:44.240 JST [29290] FATAL: the database system is shutting down
2018-02-07 14:55:44.252 JST [2179] LOG: database system is shut down
2018-02-07 14:58:44.809 JST [29302] LOG: database system was shut down at 2018-02-07 14:55:44 JST
2018-02-07 14:58:44.813 JST [29300] LOG: database system is ready to accept connections
2018-02-07 14:58:49.572 JST [29310] LOG: starting logical decoding for slot "sub1"
2018-02-07 14:58:49.572 JST [29310] DETAIL: streaming transactions committing after 0/5800EC80,
reading WAL from 0/5800EC80
2018-02-07 14:58:49.572 JST [29310] LOG: logical decoding found consistent point at 0/5800EC80
2018-02-07 14:58:49.572 JST [29310] DETAIL: There are no running transactions.
この結果より、Pu b lish er が停止した場合はレプリケーションが停止するが、再度起動すればレプリケーションも再開されることがわかりました。

6.4.2.4. 障害ケ ース 3 Subscriberのノード 停止
ロジカルレプリケーションのSu b scrib er が停止した時の挙動を確認します。

■ 初期状態
Pu b lish er のp g_stat_rep licatio n よりレプリケーションが稼働中であることを確認しました。

Page 187 of 285

PostgreSQL Enterprise Consortium(サーバ1において実施)
pubdb=# select * from pg_stat_replication;-[ RECORD 1 ]----+-----------------------------pid| 29213
usesysid| 16385
usename| repusr1
application_name | sub1
client_addr| 192.168.127.32
client_hostname |client_port| 46484
backend_start| 2018-02-07 14:33:55.842956+09
backend_xmin|
state| catchup
sent_lsn| 0/5800EBD8
write_lsn| 0/5800EBD8
flush_lsn| 0/5800EBD8
replay_lsn| 0/5800EBD8
write_lag|
flush_lag|
replay_lag|
sync_priority| 0
sync_state| async■Subscriberのノード を停止
Su b scrib er で起動中のPo stgreSQ L サーバを強制終了します。

(サーバ2において実施)-bash-4.2$ pg_ctl -m f stop
サーバ停止処理の完了を待っています....完了
サーバは停止しました
次に Pu b lish er のp g_stat_rep licatio n を確認すると、レプリケーションの情報が表示されなくなりました。 また、この時Pu b lish er のサーバログには何も出力されていませんでした。

(サーバ1において実施)
pubdb=# select * from pg_stat_replication;(0 rows)
なお、この状態のままPu b lish er で1 件データをINSERTしても特に問題なく処理は完了します。

(サーバ1において実施)
pubdb=# insert into data1 values (6, 'pub6');
INSERT 0 1
この状態から、Su b scrib er のPo stgreSQ L サーバを再度起動します。

(サーバ2において実施)-bash-4.2$ pg_ctl start
サーバの起動完了を待っています....2018-02-07 14:33:55.641 JST [10075] LOG: listening on IPv4 address"0.0.0.0", port 5432
2018-02-07 14:33:55.641 JST [10075] LOG: listening on IPv6 address "::", port 5432
2018-02-07 14:33:55.642 JST [10075] LOG: listening on Unix socket"/var/run/postgresql/.s.PGSQL.5432"
2018-02-07 14:33:55.642 JST [10075] LOG: listening on Unix socket "/tmp/.s.PGSQL.5432"
2018-02-07 14:33:55.656 JST [10075] LOG: redirecting log output to logging collector process
2018-02-07 14:33:55.656 JST [10075] HINT: Future log output will appear in directory "log".
完了
サーバ起動完了
ここで、Pu b lish er 側のp g_stat_rep licatio n を見ると初期状態の様にレプリケーションが復旧していることが確認できました。

Page 188 of 285

PostgreSQL Enterprise Consortium(サーバ1において実施)
pubdb=# select * from pg_stat_replication;-[ RECORD 1 ]----+-----------------------------pid| 29213
usesysid| 16385
usename| repusr1
application_name | sub1
client_addr| 192.168.127.32
client_hostname |client_port| 46484
backend_start| 2018-02-07 14:33:55.842956+09
backend_xmin|
state| catchup
sent_lsn| 0/5800EBD8
write_lsn| 0/5800EBD8
flush_lsn| 0/5800EBD8
replay_lsn| 0/5800EBD8
write_lag|
flush_lag|
replay_lag|
sync_priority| 0
sync_state| async
また、Pu b lish er でINSERTしたレコードがSu b scrib er のテーブルにも転送されています。

(サーバ2において実施)
subdb=# select * from data1;
c1 | c2 | c3----+------+---1 | pub1 |2 | pub2 |3 | pub3 |4 | pub4 |5 | pub5 |6 | pub6 |この時Pu b lish er 、Su b scrib er のログにはそれぞれ以下のメッセージが出力されています。

(サーバ1のサーバログ)
2018-02-07 14:33:55.845 JST [29213] LOG: starting logical decoding for slot "sub1"
2018-02-07 14:33:55.845 JST [29213] DETAIL: streaming transactions committing after 0/5800E858,
reading WAL from 0/5800E820
2018-02-07 14:33:55.845 JST [29213] LOG: logical decoding found consistent point at 0/5800E820
2018-02-07 14:33:55.845 JST [29213] DETAIL: There are no running transactions.
(サーバ2のサーバログ)
2018-02-07 14:33:55.658 JST [10077] LOG:
2018-02-07 14:33:55.659 JST [10077] LOG:
2018-02-07 14:33:55.662 JST [10075] LOG:
2018-02-07 14:33:55.674 JST [10084] LOG:
has started

database system was shut down at 2018-02-07 14:29:59 JST
recovered replication state of node 1 to 0/5800E740
database system is ready to accept connections
logical replication apply worker for subscription "sub1"

この結果より、Su b scrib er が停止した場合はレプリケーションが停止するが、再度起動すればレプリケーションも再開されることがわかりました。

6.4.2.5. 障害ケ ース 4 Publisher/Subscriber間のネット ワーク 断絶
ロジカルレプリケーションで利用するネットワークが断絶した時の挙動を確認します。

■ 初期状態
Pu b lish er のp g_stat_rep licatio n よりレプリケーションが稼働中であることを確認しました。

Page 189 of 285

PostgreSQL Enterprise Consortium(サーバ1において実施)
pubdb=# select * from pg_stat_replication;-[ RECORD 1 ]----+----------------------------pid| 29310
usesysid| 16385
usename| repusr1
application_name | sub1
client_addr| 192.168.127.32
client_hostname |client_port| 46522
backend_start| 2018-02-07 14:58:49.56556+09
backend_xmin|
state| streaming
sent_lsn| 0/5800EE40
write_lsn| 0/5800EE40
flush_lsn| 0/5800EE40
replay_lsn| 0/5800EE40
write_lag|
flush_lag|
replay_lag|
sync_priority| 0
sync_state| async■ ネット ワーク の断絶
Pu b lish er でレプリケーションに使用するネットワークを停止します。 ここではO S(Cen tO S7.2)のNetw o rkMan ager を利用して対象インタフェース(en s192)を停止しました。

(サーバ1において実施)[root@nk_PGECons_1 ~]# nmcli connection down ens192
Connection 'ens192' successfully deactivated (D-Bus active path:/org/freedesktop/NetworkManager/ActiveConnection/0)[root@nk_PGECons_1 ~]# nmcli connection show
名前
UUID
タイプ
デバイス
ens192 fa64b28e-a7db-45f0-b7b1-0b2b298b20d2 802-3-ethernet -ens160 c0791a7a-43be-4254-847d-2b793b583bba 802-3-ethernet ens160
しばらくの間はPu b lish er のp g_stat_rep licatio n が初期状態のままですが、 1 分ほど経過するとレプリケーションの情報が表示されなくなりました。

(サーバ1において実施)
pubdb=# select * from pg_stat_replication;(0 rows)
この時のPu b lish er のログには以下のメッセージが出力されました。ここではw al_sen der_timeo u t パラメータをデフォルトの60 秒で設定していたため、 タイムアウトを検知して非活動のレプリケーション接続を停止しています。

(サーバ1のサーバログ)
2018-02-07 15:41:12.504 JST [29310] LOG: terminating walsender process due to replication timeout■ ネット ワーク の復旧
Pu b lish er で停止したネットワークのインタフェースを復旧します。

(サーバ1において実施)[root@nk_PGECons_1 ~]# nmcli connection up ens192
接続が正常にアクティベートされました (D-Bus アクティブパス: /org/freedesktop/NetworkManager/ActiveConnection/2)[root@nk_PGECons_1 ~]# nmcli connection show
名前
UUID
タイプ
デバイス
ens192 fa64b28e-a7db-45f0-b7b1-0b2b298b20d2 802-3-ethernet ens192
ens160 c0791a7a-43be-4254-847d-2b793b583bba 802-3-ethernet ens160
Pu b lish er 側のp g_stat_rep licatio n を確認すると、レプリケーションの情報が再び表示されるようになりました。

Page 190 of 285

PostgreSQL Enterprise Consortium(サーバ1において実施)
pubdb=# select * from pg_stat_replication;-[ RECORD 1 ]----+-----------------------------pid| 29665
usesysid| 16385
usename| repusr1
application_name | sub1
client_addr| 192.168.127.32
client_hostname |client_port| 46523
backend_start| 2018-02-07 15:44:40.511419+09
backend_xmin|
state| catchup
sent_lsn| 0/5800F300
write_lsn| 0/5800F300
flush_lsn| 0/5800F300
replay_lsn| 0/5800F300
write_lag|
flush_lag|
replay_lag|
sync_priority| 0
sync_state| async
Pu b lish er のログには以下のメッセージが出力され、レプリケーションが再開されたことがわかります。

(サーバ1のサーバログ)
2018-02-07 15:44:40.514 JST [29665] LOG: starting logical decoding for slot "sub1"
2018-02-07 15:44:40.514 JST [29665] DETAIL: streaming transactions committing after 0/5800EE40,
reading WAL from 0/5800EE08
2018-02-07 15:44:40.514 JST [29665] LOG: logical decoding found consistent point at 0/5800EE08
2018-02-07 15:44:40.514 JST [29665] DETAIL: There are no running transactions.
また、Su b scrib er のログは以下のメッセージが出力されており、ネットワークの復旧に伴って lo gical rep licatio n w o rker が再起動したことがわかります。

(サーバ2のサーバログ)
2018-02-07 15:44:40.338 JST [10161] ERROR: could not receive data from WAL stream: server closed
the connection unexpectedlyThis probably means the server terminated abnormally
before or while processing the request.
2018-02-07 15:44:40.339 JST [10075] LOG: worker process: logical replication worker for
subscription 16463 (PID 10161) exited with exit code 1
2018-02-07 15:44:40.343 JST [10247] LOG: logical replication apply worker for subscription "sub1"
has started
これらの結果より、ネットワークの断絶により一時的にレプリケーションが停止した場合でも、ネットワークが復旧すればレプリケーションも自動的に再開されることがわかりました。

6.4.3. レプリ ケー シ ョ ン 対象テー ブル の 追加 /削除
ロジカルレプリケーションが稼働している環境で新たに作成したテーブルをレプリケーション対象として追加したり、テーブル定義を変更する手順を確認します。

6.4.3.1. 前提と する環境
本章は以下の環境を利用した検証結果を元に解説します。

Page 191 of 285

PostgreSQL Enterprise Consortium

Po stgreSQ L サーバはロジカルレプリケーションのPu b lish er1 台、Su b scrib er1 台で構成
ロジカルレプリケーションのPu b lish er （サーバ1 。IP アドレス 192 ｡168 ｡127 ｡31 ）では、新規に作成したデータベースp u b db ,p u b db _all_tab le のみを作成
ロジカルレプリケーションのSu b scrib er （サーバ2 。IP アドレス 192 ｡168 ｡127 ｡32 ）では、新規に作成したデータベースsu b db ,su b db _all_tab le のみを作成
6.4.3.2. レプリケ ーショ ン対象のテーブルを追加する手順 ( データ ベース単位の Publication)
データベース単位のPu b licatio n が作成されたデータベースに対して、テーブルを追加する手順を説明します。

■ 初期状態
Pu b lish er のデータベースp u b db _all_tab le に接続し、データベース単位のPu b licatio n p u b _all_tab le を作成します。

(サーバ1において実施)
pubdb_all_table=# CREATE PUBLICATION pub_all_table FOR ALL TABLES;CREATE PUBLICATION
pubdb_all_table=# \dRp+
Publication pub_all_table
Owner | All tables | Inserts | Updates | Deletes----------+------------+---------+---------+--------postgres | t
| t
| t
| t
Pu lish er のデータベースp u b db _all_tab le にテーブルdata1 を作成します。システムカタログp g_p u b licatio n _tab les でテーブルdata1 がPu b licatio n
p u b _all_tab le のレプリケーション対象として認識されたことが確認できます。

Page 192 of 285

PostgreSQL Enterprise Consortium(サーバ1において実施)
pubdb_all_table=# CREATE TABLE data1 (c1 INT PRIMARY KEY, c2 VARCHAR(5));
CREATE TABLE
pubdb_all_table=# SELECT * FROM pg_publication_tables;
pubname| schemaname | tablename---------------+------------+----------pub_all_table | public| data1
Su b scrib er のデータベースsu b db _all_tab le に接続し、Su b scrip tio n を作成します。なお、Su b scrip tio n 作成時点で接続先のPu b licatio n の対象テーブルが存在しないとエラーになるため、あらかじめSu b scrib er のデータベースに同一名称のテーブルを作成しておいてください。

(サーバ2において実施)
subdb_all_table=# CREATE SUBSCRIPTION sub_all_table CONNECTION 'host=192.168.127.31
dbname=pubdb_all_table user=repusr1 password=repusr1' PUBLICATION pub_all_table;
ERROR: relation "public.data1" does not exist ★data1テーブルが存在しないとエラーとなる
subdb_all_table=# CREATE TABLE data1 (c1 INT PRIMARY KEY, c2 VARCHAR(5));
CREATE TABLE
subdb_all_table=# CREATE SUBSCRIPTION sub_all_table CONNECTION 'host=192.168.127.31
dbname=pubdb_all_table user=repusr1 password=repusr1' PUBLICATION pub_all_table;
NOTICE: created replication slot "sub_all_table" on publisher
CREATE SUBSCRIPTION
Su b scrip tio n を作成するとSu b scrib er のログに初期データ転送のメッセージが出力され、ロジカルレプリケーションが開始されたことが確認できます。

(サーバ2のサーバログ)
2018-01-17 11:09:23.050 JST [31749] LOG: logical replication table synchronization worker for
subscription "sub_all_table", table "data1" has started
2018-01-17 11:09:23.062 JST [31749] LOG: logical replication table synchronization worker for
subscription "sub_all_table", table "data1" has finished■ テーブルの追加手順
初期状態から、Pu lish er のデータベースp u b db _all_tab le にテーブルdata2 を作成します。FO R ALL TAB LES 句を付けて CRATE した Pu b licatio n のため、作成したテーブルdata2 が自動的にレプリケーション対象として認識されていることがわかります。

(サーバ1において実施)
pubdb_all_table=# CREATE TABLE data2 (c1 INT PRIMARY KEY, c2 VARCHAR(5));
CREATE TABLE
pubdb_all_table=# SELECT * FROM pg_publication_tables;
pubname| schemaname | tablename---------------+------------+----------pub_all_table | public| data1
pub_all_table | public| data2
次に、Su b scrib er のデータベースsu b db _all_tab le にもテーブルdata2 を作成します。この時点ではSu b scrip tio n su b _all_tab le にテーブルdata2 は認識されておらず、テーブルdata2 のレプリケーションが開始されていないことがわかります。

(サーバ2において実施)
subdb_all_table=# CREATE TABLE data2 (c1 INT PRIMARY KEY, c2 VARCHAR(5));
CREATE TABLE
subdb_all_table=# select a3.subname, a2.relname, a1.srsubstate, a1.srsublsn frompg_subscription_rel as a1 left outer join pg_class as a2 on a1.srrelid = a2.oid left outer join
pg_stat_subscription as a3 on a1.srsubid = a3.subid;
subname| relname | srsubstate | srsublsn---------------+---------+------------+-----------sub_all_table | data1 | r| 0/5602B818
既存のSu b scrip tio n に新たなレプリケーション対象テーブルを認識させるために、ALTER SU B SCRIPTIO N 文を実行します。再びp g_su b scrip tio n _rel を確認するとテー
ブルdata2 が新たに認識されていることが確認できます。

Page 193 of 285

PostgreSQL Enterprise Consortium(サーバ2において実施)
subdb_all_table=# ALTER SUBSCRIPTION sub_all_table REFRESH PUBLICATION;
ALTER SUBSCRIPTION
subdb_all_table=# select a3.subname, a2.relname, a1.srsubstate, a1.srsublsn frompg_subscription_rel as a1 left outer join pg_class as a2 on a1.srrelid = a2.oid left outer join
pg_stat_subscription as a3 on a1.srsubid = a3.subid;
subname| relname | srsubstate | srsublsn---------------+---------+------------+-----------sub_all_table | data1 | r| 0/5602B818
sub_all_table | data2 | d|
6.4.3.3. レプリケ ーショ ン対象のテーブルを追加する手順 ( テーブル単位の Publication)
テーブル単位のPu b licatio n が作成されたデータベースに対して、テーブルを追加する手順を説明します。

■ 初期状態
Pu b lish er のデータベースp u b db にテーブルdata1 をCREATE した後、レプリケーション対象とするPu b licatio n p u b 1 を作成します。

(サーバ1において実施)
pubdb=# CREATE TABLE data1 (c1 INT PRIMARY KEY, c2 VARCHAR(5));
CREATE TABLE
pubdb=> CREATE PUBLICATION pub1 FOR TABLE data1;
CREATE PUBLICATION
pubdb=# \dRp+
Publication pub1
Owner | All tables | Inserts | Updates | Deletes---------+------------+---------+---------+--------pubusr1 | f
| t
| t
| t
Tables:"public.data1"
Su b scrib er のデータベースsu b db に Su b scrip tio n を作成し、レプリケーションを開始します。

(サーバ2において実施)
subdb=# CREATE TABLE data1 (c1 INT PRIMARY KEY, c2 VARCHAR(5));
CREATE TABLE
subdb=# CREATE SUBSCRIPTION sub1 CONNECTION 'host=192.168.127.31 dbname=pubdb user=repusr1
password=repusr1' PUBLICATION pub1;
NOTICE: created replication slot "sub1" on publisher
CREATE SUBSCRIPTION■ テーブルの追加手順
初期状態から、Pu lish er のデータベースp u b db にテーブルdata1_1 を作成します。FO R ALL TAB LES 句を付けずに CRATE した Pu b licatio n のため、作成したテーブル
data1_1 がレプリケーション対象と認識されていないことがわかります。

(サーバ1において実施)
pubdb=# CREATE TABLE data1_1 (c1 INT PRIMARY KEY, c2 VARCHAR(5));
CREATE TABLE
pubdb=# SELECT * FROM pg_publication_tables;
pubname | schemaname | tablename----------+------------+----------pub1| public| data1
テーブルdata1_1 を既存のPu b licatio n p u b 1 にレプリケーション対象として追加するには、ALTER PU B LICATIO N 文を実行します。

Page 194 of 285

PostgreSQL Enterprise Consortium(サーバ1において実施)
pubdb=# ALTER PUBLICATION pub1 ADD TABLE data1_1;
ALTER PUBLICATION
pubdb=# SELECT * FROM pg_publication_tables;
pubname | schemaname | tablename---------+------------+----------pub1| public| data1
pub1| public| data1_1
次に、Su b scrib er のデータベースsu b db にテーブルdata1_1 を作成します。この時点ではSu b scrip tio n su b 1 にテーブルdata1_1 は認識されません。

(サーバ2において実施)
subdb=# CREATE TABLE data1_1 (c1 INT PRIMARY KEY, c2 VARCHAR(5));
CREATE TABLE
subdb=# select a3.subname, a2.relname, a1.srsubstate, a1.srsublsn from pg_subscription_rel as a1
left outer join pg_class as a2 on a1.srrelid = a2.oid left outer join pg_stat_subscription as a3 on
a1.srsubid = a3.subid;
subname | relname | srsubstate | srsublsn---------+---------+------------+-----------sub1| data1 | r| 0/55FB63D0
既存のSu b scrip tio n に新たなレプリケーション対象テーブルを認識させるために、ALTER SU B SCRIPTIO N 文を実行します。p g_su b scrip tio n _rel を確認するとテーブル
data1_1 が新たに認識されていることが確認できます。

(サーバ2において実施)
subdb=# ALTER SUBSCRIPTION sub1 REFRESH PUBLICATION;
ALTER SUBSCRIPTION
subdb=# select a3.subname, a2.relname, a1.srsubstate, a1.srsublsn from pg_subscription_rel as a1
left outer join pg_class as a2 on a1.srrelid = a2.oid left outer join pg_stat_subscription as a3 on
a1.srsubid = a3.subid;
subname | relname | srsubstate | srsublsn---------+---------+------------+-----------sub1| data1_1 | r| 0/56068D38
sub1| data1 | r| 0/55FB63D0
6.4.3.4. レプリケ ーショ ン対象のテーブルを変更する手順
レプリケーションが実行されているテーブルに新たな列を追加する手順を説明します。

■ 初期状態
前述のPu b licatio n p u b 1, Su b scrip tio n su b 1 を利用し、テーブルdata1 のデータがPu b lish er,Su b scrib er 間でレプリケーションされている状態とします。また、テーブ
ルdata1 にはそれぞれ1 件のデータが存在する状態とします。

(サーバ1において実施)
pubdb=# SELECT * FROM data1;
c1 | c2----+-----1 | pub1
(サーバ2において実施)
subdb=# SELECT * FROM data1;
c1 | c2----+-----1 | pub1
■ 列の追加手順
レプリケーション対象のテーブル定義に不一致があると更新競合が発生し、ロジカルレプリケーションの停止につながります。意図しないエラーを起こさぬようあらかじめALTERSU B SCRIPTIO N 文でSu b scrip tio n を一時停止しておくとよいでしょう。

Page 195 of 285

PostgreSQL Enterprise Consortium(サーバ2において実施)
subdb=# ALTER SUBSCRIPTION sub1 DISABLE;
ALTER SUBSCRIPTION
subdb=# SELECT * FROM pg_stat_subscription;subid |
subname| pid | relid | received_lsn |last_msg_send_time|
last_msg_receipt_time| latest_end_lsn |latest_end_time-------+---------------+-------+-------+--------------+-------------------------------+------------------------------+----------------+------------------------------16463 | sub1
|
|
|
|
|
|
|
この時、Su b scrib er のログには以下の様なメッセージが出力されます。

(サーバ2のサーバログ)
2018-01-17 14:51:52.437 JST [31708] LOG: logical replication apply worker for subscription "sub1"
will stop because the subscription was disabled
ロジカルレプリケーションを停止した後、Pu b lish er のテーブルdata1 に ALTER TAB LE 文で列を追加します。また、ここで確認のため1 件レコードを挿入しておきます。

(サーバ1において実施)
pubdb=# ALTER TABLE data1 ADD COLUMN c3 VARCHAR(5);ALTER TABLE
pubdb=# INSERT INTO data1 VALUES (2, 'pub2', 'pub2');
INSERT 0 1
pubdb=# SELECT * FROM data1;
c1 | c2 | c3----+------+-----1 | pub1 |2 | pub2 | pub2
次に Su b scrib er のテーブルdata1 にもALTER TAB LE 文で同じ定義の列を追加します。Su b scrip tio n を停止しているため2 件目のレコードはまだ転送されていません。

(サーバ2において実施)
subdb=# ALTER TABLE data1 ADD COLUMN c3 VARCHAR(5);ALTER TABLE
subdb=# SELECT * FROM data1;
c1 | c2 | c3----+------+---1 | pub1 |Su b scrib er でSu b scrip tio n を再度有効化し、ロジカルレプリケーションを再開すると、この時点で2 件目のレコードが転送されます。

(サーバ2において実施)
subdb=# ALTER SUBSCRIPTION sub1 ENABLE;
ALTER SUBSCRIPTION
subdb=# SELECT * FROM data1;
c1 | c2 | c3----+------+-----1 | pub1 |2 | pub2 | pub2
Su b scrib er のp g_stat_su b scritp io n とログを再度確認すると、正常にレプリケーションが再開されていることがわかります。

Page 196 of 285

PostgreSQL Enterprise Consortium(サーバ2において実施)
subdb=# SELECT * FROM pg_stat_subscription;subid |
subname| pid | relid | received_lsn |last_msg_send_time|
last_msg_receipt_time| latest_end_lsn |latest_end_time-------+---------------+-------+-------+--------------+-------------------------------+------------------------------+----------------+------------------------------16463 | sub1
| 32626 || 0/56073298 | 2018-01-17 15:02:10.817291+09 | 2018-01-17
15:02:10.650377+09 | 0/56073298| 2018-01-17 15:02:10.817291+09(サーバ2のサーバログ)
2018-01-17 15:01:47.266 JST [32626] LOG: logical replication apply worker for subscription "sub1"
has started
6.4.3.5. レプリケ ーショ ン対象のテーブルに イ ンデック スを追加する手順
レプリケーションが実行されているテーブルにインデックスを追加する手順を説明します。

■ 初期状態
前述のPu b licatio n p u b 1, Su b scrip tio n su b 1 およびテーブルdata1 をそのまま利用します。初期状態でPu b lish er,Su b scrib er のテーブルdata1 は以下の定義となっています。

(サーバ1において実施)
pubdb=# \d data1
Table "public.data1"
Column |Type| Collation | Nullable | Default--------+----------------------+-----------+----------+--------c1| integer|| not null |c2| character varying(5) |||c3| character varying(5) |||Indexes:"data1_pkey" PRIMARY KEY, btree (c1)
Publications:"pub1"(サーバ2において実施)
subdb=# \d data1;
Table "public.data1"
Column |Type| Collation | Nullable | Default--------+----------------------+-----------+----------+--------c1| integer|| not null |c2| character varying(5) |||c3| character varying(5) |||Indexes:"data1_pkey" PRIMARY KEY, btree (c1)
■Publisherのテーブルに イ ンデック スを追加
初期状態から Pu b lish er のテーブルdata1 にのみインデックスをCREATE すると、Su b scrib er のテーブルdata1 にはインデックスが追加されないことがわかります。

Page 197 of 285

PostgreSQL Enterprise Consortium(サーバ1において実施)
pubdb=# CREATE INDEX data1_c2_idx ON data1 (c2);
CREATE INDEX
pubdb=# \d data1
Table "public.data1"
Column |Type| Collation | Nullable | Default--------+----------------------+-----------+----------+--------c1| integer|| not null |c2| character varying(5) |||c3| character varying(5) |||Indexes:"data1_pkey" PRIMARY KEY, btree (c1)"data1_c2_idx" btree (c2)
Publications:"pub1"(サーバ2において実施)
subdb=# \d data1;
Table "public.data1"
Column |Type| Collation | Nullable | Default--------+----------------------+-----------+----------+--------c1| integer|| not null |c2| character varying(5) |||c3| character varying(5) |||Indexes:"data1_pkey" PRIMARY KEY, btree (c1)
■Subscriberのテーブルに イ ンデック スを追加
次に Su b scrib er のテーブルdata1 にのみインデックスをCREATE すると、Su b scrib er のテーブルdata1 にのみインデックスが追加されていることがわかります。

(サーバ2において実施)
subdb=# CREATE INDEX data1_c3_idx ON data1 (c3);
CREATE INDEX
subdb=# \d data1;
Table "public.data1"
Column |Type| Collation | Nullable | Default--------+----------------------+-----------+----------+--------c1| integer|| not null |c2| character varying(5) |||c3| character varying(5) |||Indexes:"data1_pkey" PRIMARY KEY, btree (c1)"data1_c3_idx" btree (c3)
(サーバ1において実施)
pubdb=# \d data1
Table "public.data1"
Column |Type| Collation | Nullable | Default--------+----------------------+-----------+----------+--------c1| integer|| not null |c2| character varying(5) |||c3| character varying(5) |||Indexes:"data1_pkey" PRIMARY KEY, btree (c1)"data1_c2_idx" btree (c2)
Publications:"pub1"
ロジカルレプリケーションにおいてインデックスはレプリケーション対象とならないため、インデックスの追加、削除が既存のレプリケーションに影響を及ぼすことはありませんが、
Pu b lish er で作成したインデックスがSu b scrib er でも必要であれば同じCREATE INDEX文を発行する必要があります。

6.4.4. レプリ ケー シ ョ ン 対象外となる操作、 オブジェクト
ストリーミングレプリケーションがデータベースクラスタへの操作、オブジェクトを全て複製するのと違い、Po stgreSQ L10 のロジカルレプリケーションでは以下の様な操作、オブPage 198 of 285

PostgreSQL Enterprise Consortium
ジェクトが複製されません。そのためこれらは必要に応じて Pu b lish er とSu b scrib er で同じ操作を行う等の対処を求められます。
表 6.6 ロジカルレプリケーションで複製されない操作、オブジェクト
操作、オブジェ ク ト

動作検証

CREATE TAB LE, ALTER TAB LE, CREATE INDEX等のDDL
シーケンスの定義、データ■

TRU NCATE■

Large O b ject
VIEW , MATERIALIZED VIEW
FO REIG N TAB LEU NLO G G ED TAB LE
パーティショニングの親テーブル
ここでは、TRU NCATE とシーケンスに対するロジカルレプリケーションの挙動を確認します。

6.4.4.1. 前提と する環境
本章は以下の環境を利用した検証結果を元に解説します。

Po stgreSQ L サーバはロジカルレプリケーションのPu b lish er1 台、Su b scrib er1 台で構成
ロジカルレプリケーションのPu b lish er （サーバ1 。IP アドレス 192 ｡168 ｡127 ｡31 ）では、新規に作成したデータベースp u b db を作成
データベースp u b db には、レプリケーション対象のテーブルを個別定義するPu b licatio n p u b 1,p u b 3 を作成
p u b 1,p u b 3 のレプリケーション対象として、それぞれテーブルdata1,data3 を設定
ロジカルレプリケーションのSu b scrib er （サーバ2 。IP アドレス 192 ｡168 ｡127 ｡32 ）では、新規に作成したデータベースsu b db を作成
データベースsu b db には、Pu b lish er のp u b db データベースに定義した Pu b licatio n から変更データを受け取るSu b scrip tio n su b 1,su b 3 およびテー
ブルdata1,data3 を作成

Page 199 of 285

PostgreSQL Enterprise Consortium

6.4.4.2. TRUN CATE
■ 初期状態
初期状態でテーブルdata1 のデータがPu b lish er,Su b scrib er 間でレプリケーションされている状態とします。また、テーブルdata1 にはそれぞれ5 件のデータが存在する状態とします。

(サーバ1において実施)
pubdb=# select * from data1;
c1 | c2----+-----1 | pub1
2 | pub2
3 | pub3
4 | pub4
5 | pub5(サーバ2において実施)
subdb=# select * from data1;
c1 | c2----+-----1 | pub1
2 | pub2
3 | pub3
4 | pub4
5 | pub5
Pu b lish er でテーブルdata1 に対して TRU NCATE TAB LE を実行します。

(サーバ1において実施)
pubdb=# TRUNCATE TABLE data1;
TRUNCATE TABLE
pubdb=# select * from data1;
c1 | c2----+---この状態でSu b scrib er のテーブルdata1 は変化していないことが確認できます。

(サーバ2において実施)
subdb=# select * from data1;
c1 | c2----+-----1 | pub1
2 | pub2
3 | pub3
4 | pub4
5 | pub5
Su b scrib er でもテーブルdata1 に対して TRU NCATE TAB LE を実行することで、Pu b lish er と一致します。

(サーバ2において実施)
subdb=# TRUNCATE TABLE data1;
TRUNCATE TABLE
subdb=# select * from data1;
c1 | c2----+---(サーバ1において実施)
pubdb=# select * from data1;
c1 | c2----+---TRU NCATE TAB LE はW AL にも記録されるデータ更新操作であり、ストリーミングレプリケーションで複製される操作ですが、ロジカルレプリケーションでは複製されないため注意が必要です。

Page 200 of 285

PostgreSQL Enterprise Consortium

6.4.4.3. シーケ ンス
ここではSERIAL 列を含むテーブルをロジカルレプリケーションで複製した場合の挙動を確認します。

■ 初期状態
Pu b lish er のデータベースp u b db 、Su b scrib er のデータベースsu b db にそれぞれテーブルdata3 を作成し、ロジカルレプリケーションで複製される状態にします。 また、テーブルdata3 にはSERIAL 列を定義します。

(サーバ1において実施)
pubdb=# CREATE TABLE data3 (c1 SERIAL PRIMARY KEY, c2 VARCHAR(5));
CREATE TABLE
pubdb=# CREATE PUBLICATION pub3 FOR TABLE data3;
CREATE PUBLICATION(サーバ2において実施)
subdb=# CREATE TABLE data3 (c1 SERIAL PRIMARY KEY, c2 VARCHAR(5));
CREATE TABLE
subdb=# CREATE SUBSCRIPTION sub3 CONNECTION 'host=192.168.127.31 dbname=pubdb user=repusr1
password=repusr1' PUBLICATION pub3;
NOTICE: created replication slot "sub3" on publisher
CREATE SUBSCRIPTION■SERIAL 列のレプリケ ーショ ン
Pu b lish er のテーブルdata3 にレコードを挿入します。挿入したレコードがSu b scrib er のテーブルdata3 にも転送されます。

(サーバ1において実施)
pubdb=# INSERT INTO data3 (c2) VALUES ('pub1');
INSERT 0 1
pubdb=# SELECT * FROM data3;
c1 | c2----+-----1 | pub1
(サーバ2において実施)
subdb=# SELECT * FROM data3;
c1 | c2----+-----1 | pub1
この時点でPu b lish er に存在するシーケンスは現在値が1 になっていますが、Su b scrib er に存在するシーケンスからは現在値が取得できません。 つまり、SERIAL 列の値自体は複製されたものの、内部的に作成されたシーケンスのデータは複製されていません。

(サーバ1において実施)
pubdb=# SELECT currval('data3_c1_seq');
currval--------1(サーバ2において実施)
subdb=# SELECT currval('data3_c1_seq');
ERROR: currval of sequence "data3_c1_seq" is not yet defined in this session
ここでSu b scrib er のテーブルdata3 に対してレコードを挿入するとキー重複でエラーになります。 これはSu b scrib er のシーケンスに Pu b lish er のシーケンスデータと関係なく
1 を発行したためです。

Page 201 of 285

PostgreSQL Enterprise Consortium(サーバ2において実施)
subdb=# INSERT INTO data3 (c2) VALUES ('pub1');
ERROR: duplicate key value violates unique constraint "data3_pkey"
DETAIL: Key (c1)=(1) already exists.
subdb=# SELECT currval('data3_c1_seq');
currval--------1
この状態で再度Su b scrib er のテーブルdata3 にレコードを挿入すると成功します。これは先ほどINSERTに失敗した際に Su b scrib er のシーケンスは1 にカウントアップされており、今回のINSERTで2 を発行したためキー重複が発生せず挿入できたためです。

(サーバ2において実施)
subdb=# INSERT INTO data3 (c2) VALUES ('pub1');
INSERT 0 1
subdb=# SELECT * FROM data3;
c1 | c2----+-----1 | pub1
2 | pub1
subdb=# SELECT currval('data3_c1_seq');
currval--------2
この時点でPu b lish er のシーケンスは現在値が1 のため、このままPu b lish er のテーブルdata3 に INSERTするとSu b scrib er のテーブルでキー重複が発生します。 そこで、
Pu b lish er のシーケンスの現在値を2 に進めてからレコード挿入するとPu b lish er で挿入したレコードがSu b scrib er に複製されました。

(サーバ1において実施)
pubdb=# SELECT * FROM data3;
c1 | c2----+-----1 | pub1
pubdb=# SELECT currval('data3_c1_seq');
currval--------1
pubdb=# SELECT nextval('data3_c1_seq');
nextval--------2
pubdb=# INSERT INTO data3 (c2) VALUES ('pub1');
INSERT 0 1
pubdb=# SELECT * FROM data3;
c1 | c2----+-----1 | pub1
3 | pub1(サーバ1において実施)
subdb=# SELECT * FROM data3;
c1 | c2----+-----1 | pub1
2 | pub1
3 | pub1
このように、Pu b lish er でSERIAL 列に付与された値はSu b scrib er のテーブルに転送されますが、内部的に使用するシーケンスのデータは複製されないため、Su b scrib er
で独自にレコードを挿入するとシーケンスのデータに不整合が生じます。そのため、SERIAL 列を含むテーブルでロジカルレプリケーションを行う場合はSu b scrib er でのレコード更新は抑制した方がよいでしょう。

6.4.5. 更新競合時の 動作
マスタしか更新できないストリーミングレプリケーションに対し、ロジカルレプリケーションはSu b scrib er も更新することができます。ただし、Pu b lish er とSu b scrib er のテーブル

Page 202 of 285

PostgreSQL Enterprise Consortium間で以下の様な更新処理の競合が発生する可能性があります。
表 6.7 ロジカルレプリケーションで発生する更新処理の競合パターン
競合パタ ーン

動作検証 ( 更新時 )動作検証 ( 初期データ 同期時 )主キー違反/一意キー違反■■

CHECK制約違反■■

更新データが存在しない■■

削除データが存在しない■■

テーブルが存在しない■■

一部の列が存在しない■■

データ型変換エラー■■

テーブルのロック■■

更新対象レコードのロック■■

ここでは競合が発生した時の挙動と対応方法について検証します。

6.4.5.1. 前提と する環境
本章は以下の環境を利用した検証結果を元に解説します。

Po stgreSQ L サーバはロジカルレプリケーションのPu b lish er1 台、Su b scrib er1 台で構成
ロジカルレプリケーションのPu b lish er （サーバ1 。IP アドレス 192 ｡168 ｡127 ｡31 ）では、新規に作成したデータベースp u b db を作成
データベースp u b db には、レプリケーション対象のテーブルを個別定義するPu b licatio n p u b 1 を作成
p u b 1 のレプリケーション対象として、それぞれテーブルdata1 を設定
ロジカルレプリケーションのSu b scrib er （サーバ2 。IP アドレス 192 ｡168 ｡127 ｡32 ）では、新規に作成したデータベースsu b db を作成

Page 203 of 285

PostgreSQL Enterprise Consortium
データベースsu b db には、Pu b lish er のp u b db データベースに定義した Pu b licatio n から変更データを受け取るSu b scrip tio n su b 1 およびテーブル
data1 を作成

6.4.5.2. 更新時の競合発生と 解消
前述の環境において実際に競合が発生した時の挙動と競合を解消させるための方法を競合発生のパターン毎に紹介します。

6.4.5.2.1. 主 キー違 反 /一 意 キー違 反■ 初期状態
テーブルdata1 のデータがPu b lish er,Su b scrib er 間でレプリケーションされている状態とします。また、テーブルdata1 にはc1 列をPRIMARY KEYとし、それぞれ以下のデータが存在する状態とします。

(サーバ1において実施)
pubdb=# \d data1
Table "public.data1"
Column |Type| Collation | Nullable | Default--------+----------------------+-----------+----------+--------c1| integer|| not null |c2| character varying(5) |||Indexes:"data1_pkey" PRIMARY KEY, btree (c1)
Publications:"pub1"
pubdb=# select * from data1;
c1 | c2----+------1 | aaaaa(サーバ2において実施)
subdb=# select * from data1;
c1 | c2----+------1 | aaaaa
2 | sub■ 競合の発生
Pu b lish er のテーブルdata1 に c1= 2 のレコードを挿入するとPu b lish er のINSERTは成功します。

(サーバ1において実施)
pubdb=# insert into data1 values (2, 'pub');
INSERT 0 1
pubdb=# select * from data1;
c1 | c2----+------1 | aaaaa
2 | pub
この時Su b scrib er では主キー違反による競合が発生し、レプリケーションが停止します。 Pu b lish er 、Su b scrib er のログには競合が解消するまで5 秒毎に以下のメッセージが繰り返し出力されます。

Page 204 of 285

PostgreSQL Enterprise Consortium(サーバ1のサーバログ)
2018-01-10 13:37:55.149 JST [5859] LOG: starting logical decoding for slot "sub1"
2018-01-10 13:37:55.149 JST [5859] DETAIL: streaming transactions committing after 0/55F82E30,
reading WAL from 0/55F82DF8
2018-01-10 13:37:55.149 JST [5859] LOG: logical decoding found consistent point at 0/55F82DF8
2018-01-10 13:37:55.149 JST [5859] DETAIL: There are no running transactions.
(サーバ2のサーバログ)
2018-01-10 13:37:49.965 JST [2132] ERROR: duplicate key value violates unique constraint"data1_pkey"
2018-01-10 13:37:49.965 JST [2132] DETAIL: Key (c1)=(2) already exists.
2018-01-10 13:37:49.968 JST [2131] WARNING: out of background worker slots
2018-01-10 13:37:49.968 JST [2131] HINT: You might need to increase max_worker_processes.
2018-01-10 13:37:49.968 JST [2123] LOG: worker process: logical replication worker for
subscription 16394 (PID 2132) exited with exit code 1■ 競合の解消
Su b scrib er のテーブルでキーが重複するレコードを削除すると競合が解消し、レプリケーションが再開されます。 また、Pu b lish er 、Su b scrib er へのエラーメッセージも出力されなくなります。

(サーバ2において実施)
subdb=# delete from data1 where c1 = 2;
DELETE 1
subdb=# select * from data1;
c1 | c2----+------1 | aaaaa
2 | pub

6.4.5.2.2. CHECK制 約 違 反■ 初期状態
テーブルdata1 のデータがPu b lish er,Su b scrib er 間でレプリケーションされている状態とします。また、Su b scrib er のテーブルdata1 のc1 列に CHECK 制約を付与します。

(サーバ2において実施)
subdb=# ALTER TABLE data1 ADD CONSTRAINT data1_c1_check CHECK (c1 < 10);
ALTER TABLE
subdb=# \d+ data1
Table "public.data1"
Column |Type| Collation | Nullable | Default | Storage | Stats target |Description--------+----------------------+-----------+----------+---------+----------+--------------+-----------c1| integer|| not null || plain||c2| character varying(5) |||| extended ||Indexes:"data1_pkey" PRIMARY KEY, btree (c1)
Check constraints:"data1_c1_check" CHECK (c1 < 10)■ 競合の発生
Pu b lish er のテーブルdata1 に、Su b scrib er のみに追加した CHECK制約に違反するレコードを挿入すると、Pu b lish er のINSERTは成功します。

Page 205 of 285

PostgreSQL Enterprise Consortium(サーバ1において実施)
pubdb=# insert into data1 values (10, 'pub10');INSERT 0 1
pubdb=# select * from data1;
c1 | c2----+------1 | aaaaa
2 | pub
10 | pub10
この時Su b scrib er ではCHECK 制約違反による競合が発生し、レプリケーションが停止します。 Pu b lish er 、Su b scrib er のログには競合が解消するまで5 秒毎に以下のメッセージが繰り返し出力されます。

(サーバ1のサーバログ)
2018-01-10 14:05:32.268 JST [6057] LOG: starting logical decoding for slot "sub1"
2018-01-10 14:05:32.268 JST [6057] DETAIL: streaming transactions committing after 0/55F833E8,
reading WAL from 0/55F833B0
2018-01-10 14:05:32.268 JST [6057] LOG: logical decoding found consistent point at 0/55F833B0
2018-01-10 14:05:32.268 JST [6057] DETAIL: There are no running transactions.
(サーバ2のサーバログ)
2018-01-10 14:05:32.097 JST [6020] LOG: logical replication apply worker for subscription "sub1"
has started
2018-01-10 14:05:32.102 JST [6020] ERROR: new row for relation "data1" violates check constraint"data1_c1_check"
2018-01-10 14:05:32.102 JST [6020] DETAIL: Failing row contains (10, pub10).
2018-01-10 14:05:32.103 JST [2123] LOG: worker process: logical replication worker for
subscription 16394 (PID 6020) exited with exit code 1■ 競合の解消
Su b scrib er のテーブルdata1 のCHECK制約をDRO P すると競合が解消し、レプリケーションが再開されます。 また、Pu b lish er 、Su b scrib er へのエラーメッセージも出力されなくなります。

(サーバ2において実施)
subdb=# ALTER TABLE data1 DROP CONSTRAINT data1_c1_check;
ALTER TABLE

6.4.5.2.3. 更 新 テ ゙ ータが 存 在 し な い■ 初期状態
テーブルdata1 のデータがPu b lish er,Su b scrib er 間でレプリケーションされている状態とします。また、Pu b lish er,Su b scrib er のテーブルdata1 はそれぞれ以下のレコードが存在する状態とします。

(サーバ1において実施)
pubdb=# select * from data1;
c1 | c2----+-----1 | pub1
(サーバ2において実施)
subdb=# select * from data1;
c1 | c2----+---■ 競合の発生
Pu b lish er でレコードを更新するとPu b lish er のU PDATE は成功しますが、Su b scrib er のレコードには変化がありません。また、Pu b lish er,Su b scrib er のログにエラーメッ
セージも出力されていません。

Page 206 of 285

PostgreSQL Enterprise Consortium(サーバ1において実施)
pubdb=# update data1 set c2 = 'pub1a';
UPDATE 1
pubdb=# select * from data1;
c1 | c2----+------1 | pub1a(サーバ2において実施)
subdb=# select * from data1;
c1 | c2----+---キーが一致するレコードが存在しない場合の競合は無視され、レプリケーションも停止しないことがわかります。

6.4.5.2.4. 削 除 テ ゙ ータが 存 在 し な い■ 初期状態
テーブルdata1 のデータがPu b lish er,Su b scrib er 間でレプリケーションされている状態とします。また、Pu b lish er,Su b scrib er のテーブルdata1 はそれぞれ以下のレコードが存在する状態とします。

(サーバ1において実施)
pubdb=# select * from data1;
c1 | c2----+-----1 | pub1
(サーバ2において実施)
subdb=# select * from data1;
c1 | c2----+---■ 競合の発生
Pu b lish er でレコードを削除するとPu b lish er のDELETE は成功しますが、Su b scrib er のレコードには変化がありません。また、Pu b lish er,Su b scrib er のログにエラーメッ
セージも出力されていません。

(サーバ2において実施)
subdb=# delete from data1 where c1 = 1;
DELETE 1
subdb=# select * from data1;
c1 | c2----+---subdb=# select * from data1;
c1 | c2----+---キーが一致するレコードが存在しない場合の競合は無視され、レプリケーションも停止しないことがわかります。

6.4.5.2.5. テ ーフ ゙ ルが 存 在 し な い■ 初期状態
テーブルdata1 のデータがPu b lish er,Su b scrib er 間でレプリケーションされている状態とします。

■ 競合の発生Su b scrib er でテーブルdata1 をDRO P した後、Pu b lish er でテーブルdata1 にレコードを挿入すると、Pu b lish er のINSERTは成功します。

Page 207 of 285

PostgreSQL Enterprise Consortium(サーバ2において実施)
subdb=# DROP TABLE data1;
DROP TABLE(サーバ1において実施)
pubdb=# insert into data1 values (1, 'pub1');
INSERT 0 1
pubdb=# select * from data1;
c1 | c2----+-----1 | pub1
この時Su b scrib er ではテーブル不一致による競合が発生し、レプリケーションが停止します。 Pu b lish er 、Su b scrib er のログには競合が解消するまで5 秒毎に以下のメッセージが繰り返し出力されます。

(サーバ1のサーバログ)
2018-01-10 15:09:10.872 JST [6331] LOG: starting logical decoding for slot "sub1"
2018-01-10 15:09:10.872 JST [6331] DETAIL: streaming transactions committing after 0/55F8B9E8,
reading WAL from 0/55F8B9B0
2018-01-10 15:09:10.872 JST [6331] LOG: logical decoding found consistent point at 0/55F8B9B0
2018-01-10 15:09:10.872 JST [6331] DETAIL: There are no running transactions.
(サーバ2のサーバログ)
2018-01-10 15:09:10.700 JST [6297] LOG: logical replication apply worker for subscription "sub1"
has started
2018-01-10 15:09:10.706 JST [6297] ERROR: logical replication target relation "public.data1" does
not exist
2018-01-10 15:09:10.708 JST [2123] LOG: worker process: logical replication worker for
subscription 16394 (PID 6297) exited with exit code 1■ 競合の解消
Su b scrib er でテーブルdata1 を再作成するとログにエラーは出力されなくなりますが、レプリケーションは再開されません。

(サーバ2において実施)
subdb=# CREATE TABLE data1 (c1 INT PRIMARY KEY, c2 VARCHAR(5));
CREATE TABLE
subdb=# select * from data1;
c1 | c2----+---これは、再作成したテーブルdata1 がSu b scrip tio n su b 1 からレプリケーション対象として認識されていないためと考えられます。

(サーバ2において実施)
subdb=# \dRs+
List of subscriptions
Name | Owner | Enabled | Publication | Synchronous commit |Conninfo------+----------+---------+-------------+--------------------+--------------------------------------------------------------sub1 | postgres | t| {pub1}| off| host=192.168.127.31 dbname=pubdb
user=repusr1 password=repusr1
subdb=# select a3.subname, a2.relname, a1.srsubstate, a1.srsublsn from pg_subscription_rel as a1
left outer join pg_class as a2 on a1.srrelid = a2.oid left outer join pg_stat_subscription as a3 on
a1.srsubid = a3.subid;
subname |relname| srsubstate | srsublsn---------+------------------+------------+-----------Su b scrip tio n su b 1 に対して ALTER SU B SCRIPTIO N 文を実行して、su b 1 にレプリケーション対象テーブルを再認識させると、テーブルdata1 のレプリケーションが再開されます。

Page 208 of 285

PostgreSQL Enterprise Consortium(サーバ2において実施)
subdb=# ALTER SUBSCRIPTION sub1 REFRESH PUBLICATION;
ALTER SUBSCRIPTION
subdb=# select a3.subname, a2.relname, a1.srsubstate, a1.srsublsn from pg_subscription_rel as a1
left outer join pg_class as a2 on a1.srrelid = a2.oid left outer join pg_stat_subscription as a3 on
a1.srsubid = a3.subid;
subname |relname| srsubstate | srsublsn---------+------------------+------------+-----------sub1| data1| r| 0/55F8C298

6.4.5.2.6. 一 部 の列 が 存 在 し な い■ 初期状態
テーブルdata1 のデータがPu b lish er,Su b scrib er 間でレプリケーションされている状態とします。また、Su b scrib er でテーブルdata1 のc2 列をDRO P します。

(サーバ2において実施)
subdb=# ALTER TABLE data1 DROP COLUMN c2;
ALTER TABLE
subdb=# \d+ data1;
Table "public.data1"
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description--------+---------+-----------+----------+---------+---------+--------------+------------c1| integer || not null || plain ||Indexes:"data1_pkey" PRIMARY KEY, btree (c1)
■ 競合の発生
Pu b lish er でテーブルdata1 に対してレコードを挿入すると、Pu b lish er のINSERTは成功します。

(サーバ1において実施)
pubdb=# insert into data1 values (1, 'pub1');
INSERT 0 1
pubdb=# select * from data1;
c1 | c2----+-----1 | pub1
この時Su b scrib er では列の不一致による競合が発生し、レプリケーションが停止します。 Pu b lish er 、Su b scrib er のログには競合が解消するまで5 秒毎に以下のメッセージが繰り返し出力されます。

(サーバ1のサーバログ)
2018-01-10 15:53:11.464 JST [6480] LOG: starting logical decoding for slot "sub1"
2018-01-10 15:53:11.464 JST [6480] DETAIL: streaming transactions committing after 0/55F900C0,
reading WAL from 0/55F90088
2018-01-10 15:53:11.464 JST [6480] LOG: logical decoding found consistent point at 0/55F90088
2018-01-10 15:53:11.464 JST [6480] DETAIL: There are no running transactions.
(サーバ2のサーバログ)
2018-01-10 15:53:11.284 JST [6472] LOG: logical replication apply worker for subscription "sub1"
has started
2018-01-10 15:53:11.298 JST [6472] ERROR: logical replication target relation "public.data1" ismissing some replicated columns
2018-01-10 15:53:11.299 JST [6420] LOG: worker process: logical replication worker for
subscription 16394 (PID 6472) exited with exit code 1■ 競合の解消
Su b scrib er でテーブルdata1 のc2 列を再度追加するとレプリケーションが再開されます。 また、Pu b lish er 、Su b scrib er へのエラーメッセージも出力されなくなります。

Page 209 of 285

PostgreSQL Enterprise Consortium(サーバ2において実施)
subdb=# ALTER TABLE data1 ADD COLUMN c2 VARCHAR(5);ALTER TABLE
subdb=# \d+ data1;
Table "public.data1"
Column |Type| Collation | Nullable | Default | Storage | Stats target |Description--------+----------------------+-----------+----------+---------+----------+--------------+-----------c1| integer|| not null || plain||c2| character varying(5) |||| extended ||Indexes:"data1_pkey" PRIMARY KEY, btree (c1)subdb=# select * from data1;
c1 | c2----+-----1 | pub1
■Subscriberのテーブルに 列が多く 定義さ れ て いる場合
前述のとおりSu b scrib er のテーブルに Pu b lish er のテーブルの列が存在しない場合は競合が発生します。 逆に Pu b lish er のテーブルに存在しない列がSu b scrib er のテー
ブルに存在した場合、以下のとおり競合は発生しません。 この時Su b scrib er のテーブルのみに定義された列にはNU LL が設定されます。

(サーバ1において実施)
pubdb=# \d data1
Table "public.data1"
Column |Type| Collation | Nullable | Default--------+----------------------+-----------+----------+--------c1| integer|| not null |c2| character varying(5) |||Indexes:"data1_pkey" PRIMARY KEY, btree (c1)
Publications:"pub1"(サーバ2において実施)
subdb=# ALTER TABLE data1 ADD COLUMN c3 VARCHAR(5); <-- Publisherに存在しないc3列を追加する
ALTER TABLE
subdb=# \d+ data1;
Table "public.data1"
Column |Type| Collation | Nullable | Default | Storage | Stats target |Description--------+----------------------+-----------+----------+---------+----------+--------------+-----------c1| integer|| not null || plain||c2| character varying(5) |||| extended ||c3| character varying(5) |||| extended ||Indexes:"data1_pkey" PRIMARY KEY, btree (c1)
(サーバ1において実施)
pubdb=# insert into data1 values (1, 'pub1');
INSERT 0 1
pubdb=# select * from data1;
c1 | c2----+-----1 | pub1
(サーバ2において実施)
subdb=# select * from data1;
c1 | c2 | c3----+------+-----1 | pub1 | NULL <-- c3列(Publisher未定義列)にはNULLが格納される

Page 210 of 285

PostgreSQL Enterprise Consortium
6.4.5.2.7. データ型 変 換 エ ラー

■ 初期状態
テーブルdata1 のデータがPu b lish er,Su b scrib er 間でレプリケーションされている状態とします。また、Su b scrib er でテーブルdata1 のc2 列を異なるデータ型に変更します。

(サーバ2において実施)
subdb=# ALTER TABLE data1 ALTER COLUMN c2 TYPE integer USING c2::integer;
ALTER TABLE
subdb=# \d+ data1;
Table "public.data1"
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description--------+---------+-----------+----------+---------+---------+--------------+------------c1| integer || not null || plain ||c2| integer |||| plain ||Indexes:"data1_pkey" PRIMARY KEY, btree (c1)
■ 競合の発生
Pu b lish er でテーブルdata1 に対してレコードを挿入すると、Pu b lish er のINSERTは成功します。

(サーバ1において実施)
pubdb=# insert into data1 values (1, 'pub1');
INSERT 0 1
pubdb=# select * from data1;
c1 | c2----+-----1 | pub1
この時Su b scrib er では型の不一致による競合が発生し、レプリケーションが停止します。 Pu b lish er 、Su b scrib er のログには競合が解消するまで5 秒毎に以下のメッセージが繰り返し出力されます。

(サーバ1のサーバログ)
2018-01-10 16:08:13.308 JST [6602] LOG: starting logical decoding for slot "sub1"
2018-01-10 16:08:13.308 JST [6602] DETAIL: streaming transactions committing after 0/55F943E8,
reading WAL from 0/55F943B0
2018-01-10 16:08:13.308 JST [6602] LOG: logical decoding found consistent point at 0/55F943B0
2018-01-10 16:08:13.308 JST [6602] DETAIL: There are no running transactions.
(サーバ2のサーバログ)
2018-01-10 16:08:13.136 JST [6597] LOG: logical replication apply worker for subscription "sub1"
has started
2018-01-10 16:08:13.142 JST [6597] ERROR: invalid input syntax for integer: "pub1"
2018-01-10 16:08:13.142 JST [6597] CONTEXT: processing remote data for replication target relation"public.data1" column "c2", remote type character varying, local type character varying
2018-01-10 16:08:13.143 JST [6420] LOG: worker process: logical replication worker for
subscription 16394 (PID 6597) exited with exit code 1■ 競合の解消
Su b scrib er でc2 列のデータ型を元に戻すとレプリケーションが再開されます。 また、Pu b lish er 、Su b scrib er へのエラーメッセージも出力されなくなります。

Page 211 of 285

PostgreSQL Enterprise Consortium(サーバ2において実施)
subdb=# ALTER TABLE data1 ALTER COLUMN c2 TYPE VARCHAR(5);ALTER TABLE
subdb=# \d+ data1;
Table "public.data1"
Column |Type| Collation | Nullable | Default | Storage | Stats target |Description--------+----------------------+-----------+----------+---------+----------+--------------+-----------c1| integer|| not null || plain||c2| character varying(5) |||| extended ||Indexes:"data1_pkey" PRIMARY KEY, btree (c1)subdb=# select * from data1;
c1 | c2----+-----1 | pub1
■ データ 型の暗黙的変換が可能な場合
Pu b lish er とSu b scrib er のテーブルで列のデータ型が相違する場合でも暗黙的な変換が可能であれば以下のとおり競合は発生しません。

(サーバ2において実施)
subdb=# ALTER TABLE data1 ALTER COLUMN c2 TYPE date;
ALTER TABLE
subdb=# \d+ data1;
Table "public.data1"
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description--------+---------+-----------+----------+---------+---------+--------------+------------c1| integer || not null || plain ||c2| date|||| plain ||Indexes:"data1_pkey" PRIMARY KEY, btree (c1)
(サーバ1において実施)
pubdb=# insert into data1 values (1, '2018/1/1');
INSERT 0 1
pubdb=# select * from data1;c1 |c2----+---------1 | 2018/1/1(サーバ2において実施)
subdb=# select * from data1;c1 |c2----+-----------1 | 2018-01-01 <-- 暗黙的な型変換が可能なレコードはレプリケーションされる(TEXT型 -> DATE型)

6.4.5.2.8. テ ーフ ゙ ルのロ ック■ 初期状態
テーブルdata1 のデータがPu b lish er,Su b scrib er 間でレプリケーションされている状態とします。

■ 競合の発生Su b scrib er でレプリケーション対象のテーブルdata1 を明示的にロックします。

(サーバ2において実施)
subdb=# BEGIN;
BEGIN
subdb=# LOCK TABLE data1 IN EXCLUSIVE MODE;
LOCK TABLE

Page 212 of 285

PostgreSQL Enterprise Consortium

Pu b lish er でテーブルdata1 に対してレコードを挿入すると、Pu b lish er のINSERTは成功しますが、Su b scrib er のテーブルdata1 には反映されません。 この時Pu b lish er 、Su b scrib er のログにはエラーメッセージは出力されておらず、レプリケーションがロック解放を待機した状態となっています。

(サーバ1において実施)
pubdb=# insert into data1 values (1, 'pub1');
INSERT 0 1
pubdb=# select * from data1;
c1 | c2----+-----1 | pub1
(サーバ2において実施)
subdb=# select * from data1;
c1 | c2----+---■ 競合の解消
Su b scrib er でトランザクションを終了し、ロックを解放すると、レプリケーションが再開され、データが転送されます。

(サーバ2において実施)
subdb=# COMMIT;COMMIT
subdb=# select * from data1;
c1 | c2----+-----1 | pub1

6.4.5.2.9. 更 新 対 象 レ コード のロ ック■ 初期状態
テーブルdata1 のデータがPu b lish er,Su b scrib er 間でレプリケーションされている状態とします。

■ 競合の発生Su b scrib er でテーブルdata1 のレコードをSELECT FO R U PDATE で明示的にロックします。

(サーバ2において実施)
subdb=# BEGIN;
BEGIN
subdb=# SELECT * FROM data1 WHERE c1 = 1 FOR UPDATE;LOCK TABLE
Pu b lish er で同一のレコードを更新すると、Pu b lish er のU PDATE は成功しますが、Su b scrib er のテーブルdata1 には反映されません。 この時Pu b lish er 、Su b scrib er のログにはエラーメッセージは出力されておらず、レプリケーションがロック解放を待機した状態となっています。

(サーバ1において実施)
pubdb=# update data1 SET c2 = 'pub11' where c1 = 1;
UPDATE 1
pubdb=# select * from data1;
c1 | c2----+------1 | pub11(サーバ2において実施)
subdb=# select * from data1;
c1 | c2----+-----1 | pub1
■ 競合の解消

Page 213 of 285

PostgreSQL Enterprise Consortium

Su b scrib er でトランザクションを終了し、ロックを解放すると、レプリケーションが再開され、データが転送されます。

(サーバ2において実施)
subdb=# COMMIT;COMMIT
subdb=# select * from data1;
c1 | c2----+------1 | pub11
6.4.5.3. 初期データ 同期時の競合発生と 解消
ロジカルレプリケーションの開始時に実行される初期データ同期処理においても更新時と同様の競合が発生する可能があります。以下で初期データ同期時に競合が発生した時の挙動と競合を解消させるための方法をパターン毎に紹介します。

6.4.5.3.1. 主 キー違 反 /一 意 キー違 反■ 初期状態
Pu b lish er,Su b scrib er のテーブルdata1 にはc1 列をPRIMARY KEYとし、それぞれ以下のデータが存在する状態とします。

(サーバ1において実施)
pubdb=# select * from data1;
c1 | c2----+-----1 | pub1
2 | pub2
3 | pub3(サーバ2において実施)
subdb=# select * from data1;
c1 | c2----+-----2 | pub2
■ 競合の発生Su b scrib er でSu b scrip tio n su b 1 を作成し、ロジカルレプリケーションを開始します。この時Su b scrib er では主キー違反による競合が発生し、レプリケーションが停止します。

(サーバ2において実施)
subdb=# CREATE SUBSCRIPTION sub1 CONNECTION 'host=192.168.127.31 dbname=pubdb user=repusr1
password=repusr1' PUBLICATION pub1;
NOTICE: created replication slot "sub1" on publisher
CREATE SUBSCRIPTION
Pu b lish er 、Su b scrib er のログには競合が解消するまで5 秒毎に以下のメッセージが繰り返し出力されます。

Page 214 of 285

PostgreSQL Enterprise Consortium(サーバ1のサーバログ)
2018-01-11 15:53:32.764 JST [10254] LOG: logical decoding found consistent point at 0/55FB4C50
2018-01-11 15:53:32.764 JST [10254] DETAIL: There are no running transactions.
2018-01-11 15:53:32.772 JST [10255] LOG: starting logical decoding for slot "sub1"
2018-01-11 15:53:32.772 JST [10255] DETAIL: streaming transactions committing after 0/55FB4C88,
reading WAL from 0/55FB4C50
2018-01-11 15:53:32.772 JST [10255] LOG: logical decoding found consistent point at 0/55FB4C50
2018-01-11 15:53:32.772 JST [10255] DETAIL: There are no running transactions.
2018-01-11 15:53:32.792 JST [10256] LOG: logical decoding found consistent point at 0/55FB4C88
2018-01-11 15:53:32.792 JST [10256] DETAIL: There are no running transactions.
(サーバ2のサーバログ)
2018-01-11 15:53:32.602 JST [10275] LOG: logical replication apply worker for subscription "sub1"
has started
2018-01-11 15:53:32.608 JST [10276] LOG: logical replication table synchronization worker for
subscription "sub1", table "data1" has started
2018-01-11 15:53:32.630 JST [10276] ERROR: duplicate key value violates unique constraint"data1_pkey"
2018-01-11 15:53:32.630 JST [10276] DETAIL: Key (c1)=(2) already exists.
2018-01-11 15:53:32.630 JST [10276] CONTEXT: COPY data1, line 2
2018-01-11 15:53:32.631 JST [6420] LOG: worker process: logical replication worker for
subscription 16447 sync 16424 (PID 10276) exited with exit code 1■ 競合の解消
Su b scrib er のテーブルでキーが重複するレコードを削除すると競合が解消し、レプリケーションが再開されます。 また、Pu b lish er 、Su b scrib er へのエラーメッセージも出力されなくなります。

(サーバ2において実施)
subdb=# delete from data1 where c1 = 2;
DELETE 1
subdb=# select * from data1;
c1 | c2----+-----1 | pub1
2 | pub2
3 | pub3

6.4.5.3.2. CHECK制 約 違 反
主キー違反/一意キー違反による競合と同じ挙動になると推測されるため割愛します。

6.4.5.3.3. 更 新 テ ゙ ータが 存 在 し な い
初期データ同期では発生しないケースのため割愛します。

6.4.5.3.4. 削 除 テ ゙ ータが 存 在 し な い
初期データ同期では発生しないケースのため割愛します。

6.4.5.3.5. テ ーフ ゙ ルが 存 在 し な いPu b lish er,Su b scrib er のテーブルdata1 にはc1 列をPRIMARY KEYとし、それぞれ以下のデータが存在する状態とします。

Page 215 of 285

PostgreSQL Enterprise Consortium(サーバ1において実施)
pubdb=# select * from data1;
c1 | c2----+-----1 | pub1
2 | pub2
3 | pub3(サーバ2において実施)
subdb=# select * from data1;
c1 | c2----+-----2 | pub2
Su b scrib er でテーブルをDRO P してから、Su b scrip tio n を作成するとエラーとなります。Su b scrib er のデータベースにテーブルが存在しない状態ではSu b scrip tio n を作成できないため、初期データ同期時には発生しないケースとなります。

(サーバ1において実施)
pubdb=# select * from data1;
c1 | c2----+-----1 | pub1
2 | pub2
3 | pub3(サーバ2において実施)
subdb=# drop table data1;
DROP TABLE
subdb=# CREATE SUBSCRIPTION sub1 CONNECTION 'host=192.168.127.31 dbname=pubdb user=repusr1
password=repusr1' PUBLICATION pub1;
ERROR: relation "public.data1" does not exist

6.4.5.3.6. 一 部 の列 が 存 在 し な い■ 初期状態
Pu b lish er にはテーブルdata1 を作成し、レコードが3 件存在する状態にします。Su b scrib er には列が少ないテーブルdata1 を作成します。

(サーバ1において実施)
pubdb=# CREATE TABLE data1 (c1 INT PRIMARY KEY, c2 VARCHAR(5));
CREATE TABLE
pubdb=# select * from data1;
c1 | c2----+-----1 | pub1
2 | pub2
3 | pub3(サーバ2において実施)
subdb=# CREATE TABLE data1 (c1 INT PRIMARY KEY);
CREATE TABLE■ 競合の発生Su b scrib er でSu b scrip tio n su b 1 を作成し、ロジカルレプリケーションを開始します。この時Su b scrib er では列定義の相違による競合が発生し、レプリケーションが停止します。

(サーバ2において実施)
subdb=# CREATE SUBSCRIPTION sub1 CONNECTION 'host=192.168.127.31 dbname=pubdb user=repusr1
password=repusr1' PUBLICATION pub1;
NOTICE: created replication slot "sub1" on publisher
CREATE SUBSCRIPTION

Page 216 of 285

PostgreSQL Enterprise Consortium

Pu b lish er 、Su b scrib er のログには5 秒毎に以下のメッセージが繰り返し出力されます。

(サーバ1のサーバログ)
2018-01-11 16:04:15.836 JST [10365] LOG: starting logical decoding for slot "sub1"
2018-01-11 16:04:15.836 JST [10365] DETAIL: streaming transactions committing after 0/55FB5AC0,
reading WAL from 0/55FB5A88
2018-01-11 16:04:15.836 JST [10365] LOG: logical decoding found consistent point at 0/55FB5A88
2018-01-11 16:04:15.836 JST [10365] DETAIL: There are no running transactions.
2018-01-11 16:04:15.849 JST [10366] LOG: logical decoding found consistent point at 0/55FB5AC0
2018-01-11 16:04:15.849 JST [10366] DETAIL: There are no running transactions.
(サーバ2のサーバログ)
2018-01-11 16:04:15.666 JST [10385] LOG: logical replication apply worker for subscription "sub1"
has started
2018-01-11 16:04:15.673 JST [10386] LOG: logical replication table synchronization worker for
subscription "sub1", table "data1" has started
2018-01-11 16:04:15.686 JST [10386] ERROR: logical replication target relation "public.data1" ismissing some replicated columns
2018-01-11 16:04:15.688 JST [6420] LOG: worker process: logical replication worker for
subscription 16454 sync 16449 (PID 10386) exited with exit code 1
Su b scrib er でテーブルdata1 を参照しても、初期データは同期されていません。

(サーバ2において実施)
subdb=# select * from data1;c1---■ 競合の解消
更新時の競合発生と同じく、Su b scrib er でテーブルdata1 のc2 列を追加するとレプリケーションが再開されます。

6.4.5.3.7. データ型 変 換 エ ラー
一部の列が存在しないパターンと同じ挙動になると推測されるため割愛します。

6.4.5.3.8. テ ーフ ゙ ルのロ ック■ 初期状態
Pu b lish er,Su b scrib er のテーブルdata1 にはそれぞれ以下のデータが存在する状態とします。

(サーバ1において実施)
pubdb=# select * from data1;
c1 | c2----+-----1 | pub1
2 | pub2
3 | pub3(サーバ2において実施)
subdb=# select * from data1;
c1 | c2----+---■ 競合の発生
Su b scrib er でテーブルdata1 をLO CK TAB LE 〜 IN EXCLU SIVE MO DE で明示的にロックします。

Page 217 of 285

PostgreSQL Enterprise Consortium(サーバ2において実施)
subdb=# BEGIN;
BEGIN
subdb=# LOCK TABLE data1 IN EXCLUSIVE MODE;
LOCK TABLE
Su b scrib er で上記のトランザクションを実行したまま、別のコンソールでSu b scrip tio n を作成します。この時レプリケーションはロックを待機するため、テーブルdata1 には初期データが同期されていないことがわかります。

(サーバ2において実施)
subdb=# CREATE SUBSCRIPTION sub1 CONNECTION 'host=192.168.127.31 dbname=pubdb user=repusr1
password=repusr1' PUBLICATION pub1;
NOTICE: created replication slot "sub1" on publisher
CREATE SUBSCRIPTION
subdb=# select * from data1;
c1 | c2----+---■ 競合の解消
Su b scrib er でトランザクションを終了するとロックが解放されて、レプリケーションの初期データ同期が開始されます。

(サーバ2において実施)
subdb=# COMMIT;COMMIT
subdb=# select * from data1;
c1 | c2----+-----1 | pub1
2 | pub2
3 | pub3

6.4.5.3.9. 更 新 対 象 レ コード のロ ック
初期データ同期では発生しないケースのため割愛します。

6.4.5.4. WAL のスキップに よる競合の解消
前述の様に Su b scrib er で競合の原因となる要素を取り除く方法とは別に、競合が発生するW AL のSu b scrib er への適用をスキップする方法について以下に紹介します。

■ 初期状態
テーブルdata1 のデータがPu b lish er,Su b scrib er 間でレプリケーションされている状態とします。また、テーブルdata1 にはレコードが5 件存在し、Pu b lish er のW AL のLSN
は「 0/55FB 6948 」となっています。

Page 218 of 285

PostgreSQL Enterprise Consortium(サーバ1において実施)
pubdb=# select * from data1;
c1 | c2----+-----1 | pub1
2 | pub2
3 | pub3
4 | pub4
5 | pub5
pubdb=# SELECT pg_current_wal_lsn() ;pg_current_wal_lsn-------------------0/55FB6948(サーバ2において実施)
subdb=# select * from data1;
c1 | c2----+-----1 | pub1
2 | pub2
3 | pub3
4 | pub4
5 | pub5
subdb=# SELECT oid, subname FROM pg_subscription ;oid | subname-------+--------16463 | sub1
subdb=# SELECT * FROM pg_replication_origin_status;
local_id | external_id | remote_lsn | local_lsn----------+-------------+------------+-----------1 | pg_16463| 0/55FB6948 | 0/7360A120■ 競合の発生Su b scrib er でテーブルdata1 に 6 件目のレコードを挿入した後、Pu b lish er で同一キーのレコードを挿入し、競合を発生させます。

(サーバ2において実施)
subdb=# insert into data1 values (6, 'sub6');
INSERT 0 1
subdb=# select * from data1;
c1 | c2----+-----1 | pub1
2 | pub2
3 | pub3
4 | pub4
5 | pub5
6 | sub6(サーバ1において実施)
pubdb=# insert into data1 values (6, 'pub6');
INSERT 0 1
pubdb=# select * from data1;
c1 | c2----+-----1 | pub1
2 | pub2
3 | pub3
4 | pub4
5 | pub5
6 | pub6
競合が発生しているため、Pu b lish er のLSN までSu b scrib er のLSN(remo te_lsn )が到達していません。

Page 219 of 285

PostgreSQL Enterprise Consortium(サーバ1において実施)
pubdb=# SELECT pg_current_wal_lsn() ;pg_current_wal_lsn-------------------0/55FB6DB0
・ SUBSCRIPTION側でremote_lsnを確認(サーバ2において実施)
subdb=# SELECT * FROM pg_replication_origin_status;
local_id | external_id | remote_lsn | local_lsn----------+-------------+------------+-----------1 | pg_16463| 0/55FB6910 | 0/7360A120■ 競合の解消
Su b scrib er でp g_rep licatio n _o rigin _advan ce 関数を実行しLSN を進めることで、競合が発生しているW AL をスキップさせます。この時点でSu b scrib er のサーバログにはレプリケーションが停止した際のエラーメッセージが出力されなくなります。

(サーバ2において実施)
subdb=# SELECT pg_replication_origin_advance ('pg_16463', '0/55FB6DB0');
pg_replication_origin_advance------------------------------Su b scrib er でLSN を再度確認するとPu b lish er のLSN と一致していることがわかります。なお、競合発生直前に挿入したレコード(c1= 6, c2= su b 6)はそのまま残されています。

(サーバ2において実施)
subdb=# SELECT * FROM pg_replication_origin_status;
local_id | external_id | remote_lsn | local_lsn----------+-------------+------------+-----------1 | pg_16463| 0/55FB6DB0 | 0/7360A120
subdb=# select * from data1;
c1 | c2----+-----1 | pub1
2 | pub2
3 | pub3
4 | pub4
5 | pub5
6 | sub6
この状態でPu b lish er に新しいレコードを挿入すると、Su b scrib er のc1= 6 のレコードは残されたままレプリケーションは再開されており、Pu b lish er でレコード(c1= 6,c2= p u b 6)を挿入した W AL のみがスキップされたことがわかります。

Page 220 of 285

PostgreSQL Enterprise Consortium(サーバ1において実施)
pubdb=# insert into data1 values (7, 'pub7');
INSERT 0 1
pubdb=# select * from data1;
c1 | c2----+-----1 | pub1
2 | pub2
3 | pub3
4 | pub4
5 | pub5
6 | pub6
7 | pub7(サーバ2において実施)
subdb=# select * from data1;
c1 | c2----+-----1 | pub1
2 | pub2
3 | pub3
4 | pub4
5 | pub5
6 | sub6
7 | pub7

6.4.6. ストリ ー ミン グレプリ ケー シ ョ ン との 併用
ここでは、ロジカルレプリケーションとストリーミングレプリケーションを併用する環境を想定し、以下の観点を確認します。
ストリーミングレプリケーションを併用する環境の構築手順
ストリーミングレプリケーションを併用する環境で障害が発生した場合の運用手順

6.4.6.1. 前提と する環境
本章は以下の環境を利用した検証結果を元に解説します。

Po stgreSQ L サーバは3 台構成
ロジカルレプリケーションは、Pu b lish er1 台（サーバ1 。IP アドレス 192 ｡168 ｡127 ｡31 ）、Su b scrib er1 台（サーバ2 。IP アドレス 192 ｡168 ｡127 ｡32 ）で構成

Page 221 of 285

PostgreSQL Enterprise Consortium
ストリーミングレプリケーションは、マスタ 1 台（ Pu b lish er と同一）、スタンバイ1 台（サーバ3 。IP アドレス 192 ｡168 ｡127 ｡33 ）で構成
ロジカルレプリケーションのPu b lish er では、データベースクラスタ内に 2 つのデータベースを作成
p u b db : レプリケーション対象のテーブルを個別定義するPu b licatio n を作成
p u b db _all_tab le: データベース内に存在する全テーブルをレプリケーション対象とするPu b licatio n を作成
ロジカルレプリケーションのSu b scrib er では、データベースクラスタ内に 2 つのデータベースを作成
su b db : Pu b lish er のp u b db データベースに定義した Pu b licatio n から変更データを受け取るSu b scrip tio n を作成
su b db _all_tab le: Pu b lish er のp u b db _all_tab le データベースに定義した Pu b licatio n から変更データを受け取るSu b scrip tio n を作成

6.4.6.2. スト リーミ ングレプリケ ーショ ンを併用する環境の構築手順
既にロジカルレプリケーションが稼働している環境に対してストリーミングレプリケーションを稼働させる手順は、新規でストリーミングレプリケーションを稼働させる手順と変わりません。(後で「 SR 環境の設定手順」への相互参照リンクを貼る)の手順にしたがってストリーミングレプリケーションのマスタ、スレーブを構築して下さい。

6.4.6.3. スト リーミ ングレプリケ ーショ ンを併用する環境で障害が発生し た 場合の運用手順■ 初期状態
Pu b lish er にはデータベースp u b db とp u b db _all_tab le が存在します。また、p u b db データベース、p u b db _all_tab le データベースには、Pu b licatio n がそれぞれ2 つ、1つ存在します。

(サーバ1において実施)
pubdb=# \dRp+
Publication pub1-[ RECORD 1 ]------Owner| pubusr1
All tables | f
Inserts| t
Updates| t
Deletes| t
Tables:"public.data1""public.data1_1"
Publication pub2-[ RECORD 1 ]------Owner| pubusr1
All tables | f
Inserts| t
Updates| t
Deletes| t
Tables:"public.data2"
pubdb_all_table=# \dRp+
Publication pub_all_table-[ RECORD 1 ]-------Owner| postgres
All tables | t
Inserts| t
Updates| t
Deletes| t
ロジカルレプリケーションのPu b lish er とストリーミングレプリケーションのマスタを兼ねるサーバ1 の動的統計情報ビューp g_stat_rep licatio n には、ロジカルレプリケーションの情報(RECO RD 1 〜3)、ストリーミングレプリケーションの情報(RECO RD 4)が確認できます。

(サーバ1において実施)
pubdb=# select * from pg_stat_replication;-[ RECORD 1 ]----+-----------------------------pid| 2054
usesysid| 16385
usename| repusr1
application_name | sub_all_tableclient_addr| 192.168.127.32
client_hostname |Page 222 of 285

PostgreSQL Enterprise Consortium

client_port| 46604
backend_start| 2018-02-08 13:42:42.520503+09
backend_xmin|
state| streaming
sent_lsn| 0/580126B0
write_lsn| 0/580126B0
flush_lsn| 0/580126B0
replay_lsn| 0/580126B0
write_lag|
flush_lag|
replay_lag|
sync_priority| 0
sync_state| async-[ RECORD 2 ]----+-----------------------------pid| 1948usesysid| 16385
usename| repusr1
application_name | sub2
client_addr| 192.168.127.32
client_hostname |client_port| 46526
backend_start| 2018-02-08 13:36:25.199985+09
backend_xmin|
state| streaming
sent_lsn| 0/580126B0
write_lsn| 0/580126B0
flush_lsn| 0/580126B0
replay_lsn| 0/580126B0
write_lag|
flush_lag|
replay_lag|
sync_priority| 0
sync_state| async-[ RECORD 3 ]----+-----------------------------pid| 1850usesysid| 16385
usename| repusr1
application_name | sub1
client_addr| 192.168.127.32
client_hostname |client_port| 46524
backend_start| 2018-02-08 13:15:08.479523+09
backend_xmin|
state| streaming
sent_lsn| 0/580126B0
write_lsn| 0/580126B0
flush_lsn| 0/580126B0
replay_lsn| 0/580126B0
write_lag|
flush_lag|
replay_lag|
sync_priority| 0
sync_state| async-[ RECORD 4 ]----+-----------------------------pid| 1846
usesysid| 16385
usename| repusr1
application_name | nk_PGECons3
client_addr| 192.168.127.33
client_hostname |client_port| 55520
backend_start| 2018-02-08 13:14:36.673678+09
backend_xmin|
state| streaming
sent_lsn| 0/580126B0
write_lsn| 0/580126B0
flush_lsn| 0/580126B0
Page 223 of 285

PostgreSQL Enterprise Consortium

replay_lsn
write_lag
flush_lag
replay_lag
sync_priority
sync_state| 0/580126B0| 00:00:00.00054| 00:00:00.000541| 00:00:00.000541| 0| async

サーバ1 のシステムカタログp g_rep licatio n _slo ts では、ロジカルレプリケーションのSu b scrip tio n と合わせて自動作成されたレプリケーションスロットの情報が確認できます。

(サーバ1において実施)
pubdb=# select * from pg_replication_slots;-[ RECORD 1 ]-------+---------------slot_name| sub2
plugin| pgoutput
slot_type| logical
datoid| 16384
database| pubdb
temporary| f
active| tactive_pid| 1948xmin|
catalog_xmin| 701
restart_lsn| 0/58017AA8
confirmed_flush_lsn | 0/58017AE0-[ RECORD 2 ]-------+---------------slot_name| sub1
plugin| pgoutput
slot_type| logical
datoid| 16384
database| pubdb
temporary| f
active| tactive_pid| 1850xmin|
catalog_xmin| 701
restart_lsn| 0/58017AA8
confirmed_flush_lsn | 0/58017AE0-[ RECORD 3 ]-------+---------------slot_name| sub_all_tableplugin| pgoutput
slot_type| logical
datoid| 16474
database| pubdb_all_tabletemporary| f
active| tactive_pid| 2054
xmin|
catalog_xmin| 701
restart_lsn| 0/58017AA8
confirmed_flush_lsn | 0/58017AE0
p u b db データベース、p u b db _all_tab le データベースそれぞれのdata1 テーブルにはあらかじめ1 件データをINSERTしておきます。

(サーバ1において実施)
pubdb=# select * from data1;
c1 | c2----+------1 | pub1
pubdb_all_table=# select * from data1;
c1 | c2----+-----1 | pub1
Su b scrib er のsu b db データベース、su b db _all_tab le データベースには、Su b scrip tio n がそれぞれ2 つ、1 つ存在します。

Page 224 of 285

PostgreSQL Enterprise Consortium(サーバ2において実施)
subdb=# \dRs+
List of subscriptions
Name | Owner | Enabled | Publication | Synchronous commit |Conninfo------+----------+---------+-------------+--------------------+--------------------------------------------------------------sub1 | postgres | t| {pub1}| off| host=192.168.127.31 dbname=pubdb
user=repusr1 password=repusr1
sub2 | postgres | t| {pub2}| off| host=192.168.127.31 dbname=pubdb
user=repusr1 password=repusr1
subdb_all_table=# \dRs+
List of subscriptions
Name| Owner | Enabled | Publication | Synchronous commit |Conninfo---------------+----------+---------+-----------------+--------------------+------------------------------------------------------------------------sub_all_table | postgres | t| {pub_all_table} | off| host=192.168.127.31
dbname=pubdb_all_table user=repusr1 password=repusr1
ロジカルレプリケーションのSu b scrib er であるサーバ2 の動的統計情報ビューp g_stat_su b scrip tio n には、ロジカルレプリケーションの情報が3 件確認できます。

(サーバ2において実施)
subdb_all_table=# select * from pg_stat_subscription;subid |
subname| pid | relid | received_lsn |last_msg_send_time|
last_msg_receipt_time| latest_end_lsn |latest_end_time-------+---------------+-------+-------+--------------+-------------------------------+------------------------------+----------------+------------------------------16463 | sub1
| 11038 || 0/58011F60 | 2018-02-08 13:42:47.590367+09 | 2018-02-08
13:42:47.423435+09 | 0/58011F60| 2018-02-08 13:42:47.590367+09
24677 | sub2
| 11072 || 0/58011F60 | 2018-02-08 13:42:47.59058+09 | 2018-02-08
13:42:47.42367+09 | 0/58011F60| 2018-02-08 13:42:47.59058+09
24678 | sub_all_table | 11158 || 0/58011F60 | 2018-02-08 13:42:47.590534+09 | 2018-02-08
13:42:47.423605+09 | 0/58011F60| 2018-02-08 13:42:47.590534+09
ストリーミングレプリケーションのスレーブであるサーバ3 では動的統計情報ビューp g_stat_w al_receiver でストリーミングレプリケーションの情報が確認できます。

(サーバ3において実施)
pubdb=# select * from pg_stat_wal_receiver;-[ RECORD 1 ]---------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------pid| 26152
status| streaming
receive_start_lsn| 0/58000000
receive_start_tli| 1
received_lsn| 0/58016A78
received_tli| 1
last_msg_send_time| 2018-02-08 13:52:22.063552+09
last_msg_receipt_time | 2018-02-08 13:52:21.953994+09
latest_end_lsn| 0/58016A78
latest_end_time| 2018-02-08 13:52:22.063552+09
slot_name|conninfo| user=repusr1 password=******** dbname=replication host=192.168.127.31
port=5432 application_name=nk_PGECons3 fallback_application_name=walreceiver sslmode=prefersslcompression=1 krbsrvname=postgres target_session_attrs=any
また、サーバ3 にはマスタであるサーバ1 で定義されているPu b licatio n がストリーミングレプリケーションで複製されています。

Page 225 of 285

PostgreSQL Enterprise Consortium(サーバ3において実施)
pubdb=# \dRp+
Publication pub1-[ RECORD 1 ]------Owner| pubusr1
All tables | f
Inserts| t
Updates| t
Deletes| t
Tables:"public.data1""public.data1_1"
Publication pub2-[ RECORD 1 ]------Owner| pubusr1
All tables | f
Inserts| t
Updates| t
Deletes| t
Tables:"public.data2"
pubdb_all_table=# \dRp+
Publication pub_all_table-[ RECORD 1 ]-------Owner| postgres
All tables | t
Inserts| t
Updates| t
Deletes| t
ただし、サーバ3 はサーバ2 とのロジカルレプリケーションは確立していないため、サーバ3 の動的統計情報ビューp g_stat_rep licatio n ,p g_rep licatio n _slo ts を見ても、
サーバ1 と同じ情報は存在しません。

(サーバ3において実施)
pubdb_all_table=# select * from pg_replication_slots;(0 rows)
pubdb_all_table=# select * from pg_stat_replication;(0 rows)■ スト リーミ ングレプリケ ーショ ンのマスタ を切り 替え る手順
初期状態の構成でストリーミングレプリケーションのマスタに障害が発生した状態を想定し、昇格したスレーブへロジカルレプリケーションを切り替える手順を説明します。切
替後の環境は以下の様になります。

Page 226 of 285

PostgreSQL Enterprise Consortium

まず、ストリーミングレプリケーションのマスタ (サーバ1)のPo stgreSQ L サーバを強制終了します。

(サーバ1において実施)-bash-4.2$ pg_ctl -m f stop
サーバ停止処理の完了を待っています....完了
サーバは停止しました
ストリーミングレプリケーションのスレーブ(サーバ3)でp g_ctl p ro mo te コマンドを実行し、マスタに昇格します。

(サーバ3において実施)-bash-4.2$ pg_ctl promote
waiting for server to promote....完了
server promoted
サーバ3 のログでマスタとして接続を受け付け始めたことが確認でき、ストリーミングレプリケーションのマスタ切替は完了します。

(サーバ3のサーバログ)
2018-02-08 15:16:34.693 JST [26148] LOG:
2018-02-08 15:16:34.693 JST [26148] LOG:
2018-02-08 15:16:34.693 JST [26148] LOG:
14:24:19.546571+09
2018-02-08 15:16:34.694 JST [26148] LOG:
2018-02-08 15:16:34.734 JST [26148] LOG:
2018-02-08 15:16:34.738 JST [26146] LOG:

received promote request
redo done at 0/58020F38
last completed transaction was at log time 2018-02-08
selected new timeline ID: 2
archive recovery complete
database system is ready to accept connections

次に、サーバ3 を新たなPu b lish er としたロジカルレプリケーションを開始させてみます。
まず、サーバ2 でALTER SU B SCRIPTIO N 文を実行して既存のSu b scrip tio n の接続先を変更してみました。

(サーバ2において実施)
subdb=# ALTER SUBSCRIPTION sub1 CONNECTION 'host=192.168.127.33 dbname=pubdb user=repusr1
password=repusr1';
ALTER SUBSCRIPTION
Su b scrib er のログを見るとサーバ3 にレプリケーションスロット su b 1 が存在しないことが原因で失敗しています。

Page 227 of 285

PostgreSQL Enterprise Consortium(サーバ2のサーバログ)
2018-02-08 15:27:04.296 JST [11029] LOG: worker process: logical replication worker for
subscription 24678 (PID 12728) exited with exit code 1
2018-02-08 15:27:04.299 JST [12729] LOG: logical replication apply worker for subscription "sub1"
has started
2018-02-08 15:27:04.302 JST [12729] ERROR: could not start WAL streaming: ERROR: replication slot
"sub1" does not exist
また、既存のSu b scrip tio n を再作成しようとしても、レプリケーションスロット su b 1 が存在しないことで削除も拒否されます。

(サーバ2において実施)
subdb=# DROP SUBSCRIPTION sub1;
ERROR: could not drop the replication slot "sub1" on publisher
DETAIL: The error was: ERROR: replication slot "sub1" does not exist
Su b scrip tio n のDRO P 時にはレプリケーションスロットも合わせて DRO P しようとします。今回の様にレプリケーションスロットが存在しないSu b scrip tio n が残存する場合は、
Su b scrip tio n を一時停止(DISAB LE)し、レプリケーションスロットとの対応を無効化(slo t_n ame = NO NE)してから DRO P する必要があります。

(サーバ2において実施)
subdb=# ALTER SUBSCRIPTION sub1 DISABLE;
ALTER SUBSCRIPTION
subdb=# ALTER SUBSCRIPTION sub1 SET (slot_name = NONE);ALTER SUBSCRIPTION
subdb=# DROP SUBSCRIPTION sub1;
DROP SUBSCRIPTION
subdb_all_table=# ALTER SUBSCRIPTION sub_all_table DISABLE;
ALTER SUBSCRIPTION
subdb_all_table=# ALTER SUBSCRIPTION sub_all_table SET (slot_name = NONE);ALTER SUBSCRIPTION
subdb_all_table=# DROP SUBSCRIPTION sub_all_table;
DROP SUBSCRIPTION
Su b scrip tio n をDRO P した後、新たに Su b scrip tio n を作り直すと、LR が開始されます。

(サーバ2において実施)
subdb=# CREATE SUBSCRIPTION sub1 CONNECTION 'host=192.168.127.33 dbname=pubdb user=repusr1
password=repusr1' PUBLICATION pub1 WITH (copy_data = false);
NOTICE: created replication slot "sub1" on publisher
CREATE SUBSCRIPTION
subdb=# CREATE SUBSCRIPTION sub2 CONNECTION 'host=192.168.127.33 dbname=pubdb user=repusr1
password=repusr1' PUBLICATION pub2 WITH (copy_data = false);
NOTICE: created replication slot "sub2" on publisher
CREATE SUBSCRIPTION(サーバ2のサーバログ)
2018-02-08 15:53:08.879 JST [13210] LOG: logical replication apply worker for subscription "sub1"
has started
2018-02-08 15:57:37.386 JST [13231] LOG: logical replication apply worker for subscription "sub2"
has started
2018-02-08 15:57:37.393 JST [13232] LOG: logical replication table synchronization worker for
subscription "sub2", table "data2" has started
2018-02-08 15:57:37.405 JST [13232] LOG: logical replication table synchronization worker for
subscription "sub2", table "data2" has finished
なお、CREATE SU B SCRIPTIO N 文に W ITH (co p y_data = false)を付与すると、ロジカルレプリケーション開始時の初期データコピーがスキップされます。試しに W ITH(co p y_data = false) を付けずに実行すると、初期データのコピー時に残存するデータとキー重複を起こし、ロジカルレプリケーションが開始されません。

Page 228 of 285

PostgreSQL Enterprise Consortium(サーバ2において実施)
subdb_all_table=# CREATE SUBSCRIPTION sub_all_table CONNECTION 'host=192.168.127.33
dbname=pubdb_all_table user=repusr1 password=repusr1' PUBLICATION pub_all_table;
NOTICE: created replication slot "sub_all_table" on publisher
CREATE SUBSCRIPTION(サーバ2のサーバログ)
2018-02-08 16:00:04.913 JST [13245] LOG: logical replication table synchronization worker for
subscription "sub_all_table", table "data1" has started
2018-02-08 16:00:04.923 JST [13246] LOG: logical replication table synchronization worker for
subscription "sub_all_table", table "data2" has started
2018-02-08 16:00:04.928 JST [13245] ERROR: duplicate key value violates unique constraint"data1_pkey"
2018-02-08 16:00:04.928 JST [13245] DETAIL: Key (c1)=(1) already exists.
2018-02-08 16:00:04.928 JST [13245] CONTEXT: COPY data1, line 1
2018-02-08 16:00:04.929 JST [11029] LOG: worker process: logical replication worker for
subscription 24685 sync 16479 (PID 13245) exited with exit code 1
2018-02-08 16:00:04.939 JST [13246] ERROR: duplicate key value violates unique constraint"data2_pkey"
2018-02-08 16:00:04.939 JST [13246] DETAIL: Key (c1)=(1) already exists.
2018-02-08 16:00:04.939 JST [13246] CONTEXT: COPY data2, line 1
SR の新マスタとなったサーバ3 の動的統計情報ビューp g_stat_rep licatio n を確認すると、サーバ3 ⇒サーバ2 のロジカルレプリケーションが確立していることがわかります。

Page 229 of 285

PostgreSQL Enterprise Consortium(サーバ3において実施)
pubdb_all_table=# select * from pg_stat_replication;-[ RECORD 1 ]----+-----------------------------pid| 26661
usesysid| 16385
usename| repusr1
application_name | sub_all_tableclient_addr| 192.168.127.32
client_hostname |client_port| 53059
backend_start| 2018-02-08 16:07:25.52522+09
backend_xmin|
state| catchup
sent_lsn| 0/58022DA0
write_lsn| 0/58022DA0
flush_lsn| 0/58022DA0
replay_lsn| 0/58022DA0
write_lag|
flush_lag|
replay_lag|
sync_priority| 0
sync_state| async-[ RECORD 2 ]----+-----------------------------pid| 26503
usesysid| 16385
usename| repusr1
application_name | sub2
client_addr| 192.168.127.32
client_hostname |client_port| 52951
backend_start| 2018-02-08 15:57:37.444994+09
backend_xmin|
state| streaming
sent_lsn| 0/58022DA0
write_lsn| 0/58022DA0
flush_lsn| 0/58022DA0
replay_lsn| 0/58022DA0
write_lag|
flush_lag|
replay_lag|
sync_priority| 0
sync_state| async-[ RECORD 3 ]----+-----------------------------pid| 26486
usesysid| 16385
usename| repusr1
application_name | sub1
client_addr| 192.168.127.32
client_hostname |client_port| 52949
backend_start| 2018-02-08 15:53:08.937385+09
backend_xmin|
state| streaming
sent_lsn| 0/58022DA0
write_lsn| 0/58022DA0
flush_lsn| 0/58022DA0
replay_lsn| 0/58022DA0
write_lag|
flush_lag|
replay_lag|
sync_priority| 0
sync_state| async
6.4.6.4. 考察
ロジカルレプリケーションとストリーミングレプリケーションを併用する場合、更新対象を一元化してデータの整合性を担保しやすくするため、ストリーミングレプリケーションのマス

Page 230 of 285

PostgreSQL Enterprise Consortium
タとロジカルレプリケーションのPu b lish er は同じサーバにした方がよいと考えます。
この場合、ストリーミングレプリケーションのマスタがスレーブに切り替わってもPu b licatio n を再定義する必要はありません。
一方でSu b scrip tio n は再作成が必要で、古いSu b scrip tio n はレプリケーションスロットとの対応を無効化してから削除する必要があります。 また、新たなSu b scrib er を作成する際は以下のいずれかの方法が選択できます。

CREATE SU B SCRIPTIO N 文に W ITH (co p y_data = false)を付けて初期データコピーを回避する。
Su b scrib er のテーブルをあらかじめTRU NCATE TAB LE で空にしてから CREATE SU B SCRIPTIO N を実行し、初期データコピーをやり直す。
前者は初期データコピーの処理が不要でロジカルレプリケーションを再開するまでの時間を短縮できますが、マスタの切替えから Su b scrip tio n の再作成までに発生する更新を複製できないデメリットがありますので、運用形態によって選択する必要がありそうです。

6.5. まとめ
本章ではまずPo stgreSQ L のロジカルレプリケーションの特徴、アーキテクチャ、ユースケース、制限事項を整理し、実際の構築例を交えてロジカルレプリケーションを設定する具体的な手順を説明しました。ストリーミングレプリケーションではマスタのベースバックアップからデータベースクラスタ単位で一致するスレーブを作成したのに対し、ロジカル
レプリケーションではPu b licatio n /Su b scrip tio n といったオブジェクトによって、テーブル単位や更新種別単位での柔軟な複製形態でのレプリケーションを構成できることがわかりました。
また、実行時パラメータについては稼働するプロセス数との関係を検証した結果を踏まえて妥当な値を算出する方法を提示しました。アーキテクチャがストリーミングレプリ
ケーションと共通な部分があるため一部の実行時パラメータはストリーミングレプリケーションで使用するプロセス数を考慮した設定が必要であることがわかりました。
次に同期レプリケーション、複数Su b scrip tio n へのレプリケーション、カスケード構成といった応用的な使い方を検証し、ロジカルレプリケーションでもストリーミングレプリケー
ションと同様の構成がとれることが確認できました。また、パーティショニングを組み合わせることで複数サーバのデータを1 つのサーバに集約するデータ統合の用途にも応用できることが確認できました。
さらにロジカルレプリケーションを実際に運用するシーンを想定し、監視方法や障害発生時の挙動、レプリケーション開始後のテーブル追加、定義変更の手順について検証しました。ストリーミングレプリケーションと違いロジカルレプリケーションではSu b scrib er を更新できる代わりに整合性をユーザが担保しなければならないため、更新時の競合が発生した場合もユーザ側で検知、解消する必要があります。ただし、現状では競合が発生した際の対応方法は限られており、運用も容易ではないため、基本的に競合が発生しない設計、運用を心がけるべきです。つまり、Su b scrib er には検索用のインデックスを付与する程度に留めてデータの更新は行わないのが望ましいと考えます。また、ストリーミングレプリケーションとロジカルレプリケーションを併用する環境での障害を想定した検証では、Su b scrip tio n を新しいPu b licatio n につなぎ直してロジカ
ルレプリケーションを再開するまでの具体的な手順を確認できました。
ロジカルレプリケーションは、アーキテクチャの面でストリーミングレプリケーションと共通する部分がありますが、データベースクラスタ単位で複製されるストリーミングレプリケーショ
ンよりも柔軟にテーブル単位、更新種別単位で複製できるようになり、レプリケーションの活用範囲が広がったといえます。その一方で、運用面ではロジカルレプリケーションの対象とならない操作やオブジェクトが存在することや、更新時の競合が発生した時の運用が全てユーザに任されている点など現状では制約事項も多く、運用面の負荷が軽減される機能強化が今後求められます。

Page 231 of 285

PostgreSQL Enterprise Consortium

7. Bi-Directional Replication (BDR)
7.1. はじめに
7.1.1. BDRの 特徴
7.1.1.1. BDR と は
B DR(B i-Directio n al Rep licatio n )は2n dQ u adran t 社によって開発された、オープンソース（ Po stgreSQ L Licen se ）のマルチマスタ・レプリケーションシステムです。 双方向の非同期論理レプリケーションを使用し、地理的に分散したクラスタで使用するために設計されています。
マルチマスタについて一般的なRDB MS の冗長化構成（マスタスレーブ構成）においては、更新処理を実行可能なサーバをマスタと呼称します。 マルチマスタとは、同一のデータを保持している
複数のDB 間（クラスタ）において、 更新処理を実施可能なサーバが複数台存在する構成を指します。 B DR では双方向にレプリケーションを実施することで、複数のサーバへの更新を可能にしています。

7.1.1.2. ユースケ ース
B DR は以下のようなケースで有用です。
1. 遠隔地や高レイテンシ環境でクラスタを構成している場合。
2. 各ノードが書き込み処理を実施する場合。
3. クラスタ間でデータが非同期であることを許容できる場合。
例としては以下のようにレスポンスを向上させるために各地でアプリケーション及びDB を動作させるようなケースが考えられます。

図 7.1 B DR ユースケース

7.1.1.3. メ カ ニズム
B DR では「 Lo gical Deco din g 」により、W AL から論理的な変更点を抽出し、 各ノードで適用することで双方向レプリケーションを実現しています。 （ Lo gical Deco din g
及びW AL の送受信はバックグラウンドワーカープロセスが実施します）
従来のトリガーを用いた双方向レプリケーションの場合、 下図のように書き込みが余分に発生（変更記録、変更反映）してしまいます。

図 7.2 トリガベースメカニズム
一方、B DR では余分な書き込みが発生せずパフォーマンス的に有利となっています。

図 7.3 B DR メカニズム
利用されているPo stgreSQ L のメカニズムの一覧です。

Page 232 of 285

PostgreSQL Enterprise Consortium
表 7.1 利用メカニズムの一覧
機能

N o.

概要

1

Even t Triggers

一つのテーブルに接続され、DML イベントのみを補足する通常のトリガとは異なり、特定データベースのDDL イベントを捕捉可能。

2

Lo gical Deco din g

SQ L を介して実行された更新処理を外部コンシューマへストリーミングするための機能。更新結果はロジカルレプリケー
ションスロットで識別され、ストリームに送出される。

3

Rep licatio n Slo ts

マスタのデータベースの変更をスレーブ側に同じ順序で適用するための機能。

4

B ackgro u n d W o rkers

ユーザ提供のコードを別プロセスで実行するように拡張する機能。B ackgro u n d W o rker プロセスはp o stgres プロセスによって起動、終了、監視される。

5

Co mmit Timestamp s

トランザクションがいつコミットされたかを確認するための機能。

6

Rep licatio n O rigin s

レプリケーションの進行状況を追跡するための機能。双方向レプリケーションにてループの防止などが可能。

7

DDL even t cap tu re実行されるDDL コマンドを返す機能。

8

gen eric W AL messages fo r
lo gical deco din g

テキストあるいはバイナリのメッセージをW AL に挿入できる仕組み（ API ）。Lo gical Deco din g 機能によって読み出されることを想定している。

7.1.1.4. 整合性
マルチマスタ構成を取る場合、各ノードが持つ情報に不整合が発生しないように管理する仕組みが必要となります。 B DR では結果整合性（ even tu ally co n sisten t ）と呼ばれる一貫性モデルを採用し、整合性を確保します。
デフォルトでは競合が発生した場合、最後の更新処理が適用されます。（ last_u p date_w in s ）競合結果はテーブル「 b dr.b dr_co n flict_h isto ry」で確認可能です。

図 7.4 結果整合性

7.1.1.5. シーケ ンス
B DR では一意な値を払い出すために、「グローバルシーケンス」、 「ステップ/オフセットシーケンス」という 2 つの手法を紹介しています。
1. グローバルシーケンス
グローバルシーケンスでは、各ノードに予め値の塊（ ch u n k ）を一定数ずつ割り振ることで値の重複を回避しています。

ch u n k を消費するとvo tin g 処理（下記参照）を行い、新たに ch u n k を割り振ります。
グローバルシーケンスは廃止予定です。（下記のステップ/オフセットシーケンスを推奨）
vo tin g 処理
"ch u n k"と呼ばれるシーケンス番号のまとまりをノードに割り当てる処理をvo tin g 処理と呼びます。 "ch u n k"が複数のノードに割り当てられないことを確認するため、ノード間で投票処理が行われており、正常に機能させるためには奇数台のノードが必要です。 過半数のノードが停止している場合は、投票処理にて過半数に到達しなくなる
ため、新しい"ch u n k"がノードに割り当てられません。 そして、"ch u n k"が枯渇した場合、n extval の実行に失敗してしまいます。

図 7.5 グローバルシーケンス

2. ステップ/オフセットシーケンス
ステップ/オフセットシーケンスでは、各ノードで通常のPo stgreSQ L シーケンスを使用します。 各シーケンスを同じ量だけ増分されるようにし、値が重複しないように設定します。

Page 233 of 285

PostgreSQL Enterprise Consortium
設計時には注意が必要です。
増分する値をある程度大きく取らないとノードの追加に対応できません。（例： 10 の増分であればノード数の追加は10 台まで）

図 7.6 ステップ/オフセットシーケンス

7.1.1.6. 比較表
類似機能及び製品との机上比較の結果です。
表 7.2 類似機能及び製品との机上比較
比較項目

N o.

BDR

SR(Hot Standby)

ロ ジカ ルレプリケ ーショ
ン

Slony

1

マルチマスタ

○

×

×

×

2

選択的レプリケーション

○

×

○

○

3

競合検知

○

×

×

○

4

カスケーディング

×

○

○

○

5

W AL ベースレプリケーション

○

○

×

○

6

DDL レプリケーション

○

○

×

×

7

自動レプリカ新テーブル

○

○

×

×

8

シーケンスレプリケーション

○

○

○

×

9

プライマリキー更新

○

○

×

○

10

同期コミット

○

○

×

○

11

外部デーモン不使用

○

○

×

○

12

レプリカへの書き込み

○

×

○

○

h ttp s://2n dqu adran t.co m/en /reso u rces/b dr/
7.1.1.7. サポート
サポートについては以下が存在します。
表 7.3 サポート
サポート

N o.

概要

1

無償サポートB DR コミュニティへのメール、B DR のG o o gle グループが存在。
email: b dr-list@ 2n dQ u adran t.co m
G o o gle グループ: h ttp s://gro u p s.go o gle.co m/a/2n dQ u adran t.co m/fo ru m/#!fo ru m/b dr-list

2

有償サポート

2n dQ u adran t 社によるサポートを受けることが可能です。
※ 2n dQ u adran t 社について
B DR の製造元で、Po stgreSQ L の専門家（コミッター等）が多数在籍する企業です。
Po stgreSQ L のコンサルティングサービス等を提供しています。
h ttp s://2n dqu adran t.co m/en /7.2. 調査、検証の目的
机上の情報整理および検証について、以下を主な目的としています。
情報整理

B DR を使用したマルチマスタ環境を構築、運用するにあたり、各種機能やパラメータの理解に必要な情報の提供
検証

B DR の動作（競合時等）や障害発生時の対処法を調査
更新性能の検証

Page 234 of 285

PostgreSQL Enterprise Consortium

7.3. 調査、検証の前提
表 7.4 調査、検証の前提
項目

説明

Po stgreSQ L バージョン 9.4.10※ 調査時点ではB DR は9.6 に対応していなかったため

B DR バージョン

1.0.2
ソースコード： h ttp s://gith u b .co m/2n dQ u adran t/b drOS

Cen tO S 7.1

構成

2 ノード構成

ダウンロードモジュールは以下になります。

p o stgresql-b dr94-b dr.x86_64 0:1.0.2-1_2n dQ u adran t.el7.cen to s
p o stgresql-b dr94.x86_64 0:9.4.10_b dr1-1_2n dQ u adran t.el7.cen to s
p o stgresql-b dr94-co n trib .x86_64 0:9.4.10_b dr1-1_2n dQ u adran t.el7.cen to s
p o stgresql-b dr94-lib s.x86_64 0:9.4.10_b dr1-1_2n dQ u adran t.el7.cen to s
p o stgresql-b dr94-server.x86_64 0:9.4.10_b dr1-1_2n dQ u adran t.el7.cen to s
以下は構成図になります。

図 7.7 調査、検証環境構成図

7.4. BDR環境構築時の設定と設定手順
7.4.1. BDR環境の 設定手順
7.4.1.1. 概要
Po stgreSQ L B DR (B i-Directio n al Rep licatio n )を利用したマルチマスタ環境の構築手順について確認します。
また、マルチマスタ環境構築後、各ノードに対して更新が実行可能かを確認します。

h ttp ://b dr-p ro ject.o rg/do cs/n ext/in stallatio n -p ackages.h tml#INSTALLATIO N-PACKAG ES-REDHAT
こちらの環境 で検証を実施しました。

7.4.1.2. 対象試験サーバ

Page 235 of 285

PostgreSQL Enterprise Consortium
表 7.5 対象試験サーバ
サーバ名

IP アド レスn o de1

192.168.0.10

n o de2

192.168.0.12

7.4.1.3. 事前確認
1. yu m が利用可能な状態であること
パッケージを入手する際に yu m を使用
設定手順
指定がない部分は、n o de1 ・ n o de2 両方で実施します。

1. 検証環境準備
B DR 検証を実施するための環境を準備します。

ssh接続を用いて、該当環境へ接続
ユーザ: root
パスワード: xxxxxxx

2. h o sts の設定
DNS の名前解決のために各サーバのホスト名を設定します。

# vi /etc/hosts[下記をファイル末尾に追加]
192.168.0.10 node1
192.168.0.12 node2

3. 関連ポートの開放
Po stgreSQ L 間のB DR 接続のために 5432 ポートの開放をします。

# firewall-cmd --permanent --add-port=5432/tcp# firewall-cmd --reload

以下のコマンドで確認します。

# firewall-cmd --list-ports
5432/tcp

4. B DR レポジトリの登録
B DR レポジトリ用のRPM をインストールします。

# RHEL/CentOS users only:# yum install http://packages.2ndquadrant.com/postgresql-bdr94-2ndquadrant/yum-reporpms/postgresql-bdr94-2ndquadrant-redhat-latest.noarch.rpm

以下のコマンドでインストール済み一覧を確認します。

Page 236 of 285

PostgreSQL Enterprise Consortium# yum list installed postgresql-bdr94-2ndquadrant-redhat.noarch 0:1.0-3[省略]
インストール済みパッケージ

postgresql-bdr94-2ndquadrant-redhat.noarch

1.0-3

installed

5. B DR インストール
登録した B DR レポジトリから B DR をインストールします。

# yum install postgresql-bdr94-bdr[省略]
インストール:postgresql-bdr94-bdr.x86_64 0:1.0.2-1_2ndQuadrant.el7.centos
依存性関連をインストールしました :postgresql-bdr94.x86_64 0:9.4.10_bdr1-1_2ndQuadrant.el7.centos
postgresql-bdr94-contrib.x86_64 0:9.4.10_bdr1-1_2ndQuadrant.el7.centos
postgresql-bdr94-libs.x86_64 0:9.4.10_bdr1-1_2ndQuadrant.el7.centos
postgresql-bdr94-server.x86_64 0:9.4.10_bdr1-1_2ndQuadrant.el7.centos
完了しました !

以下のコマンドでインストール済み一覧を確認します。

# yum list installed postgresql-bdr94-bdr.x86_64 0:1.0.2-1_2ndQuadrant.el7.centos[省略]
インストール済みパッケージ

postgresql-bdr94-bdr.x86_64 1.0.2-1_2ndQuadrant.el7.centos@postgresql-bdr94-2ndquadrant-redhat

6. 環境変数設定
データベースクラスタとコマンド実行ファイルに環境変数の設定をします。

$ vi ~/.bash_profile[下記の修正を加える]#PGDATA=/var/lib/pgsql/9.4-bdr/data
PGDATA=/var/lib/pgsql/2ndquadrant_bdr/data/[下記をファイル末尾に追加]
export PATH=/usr/pgsql-9.4/bin:$PATH

以下のコマンドで設定した環境変数を確認します。

$ exitログアウト# su - postgres$ echo $PATH/usr/pgsql-9.4/bin:/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin$ echo $PGDATA/var/lib/pgsql/2ndquadrant_bdr/data/

7. データベースクラスタ作成
B DR 検証用に新規でデータベースクラスタを作成します。

Page 237 of 285

PostgreSQL Enterprise Consortium$ mkdir -p $HOME/2ndquadrant_bdr/$ initdb -D $HOME/2ndquadrant_bdr/data -A trust -U postgres --no-locale

8. B DR のパラメータ設定(p o stgresql.co n f)
B DR のパラメータ設定(p o stgresql.co n f)をします。

$ vi /var/lib/pgsql/2ndquadrant_bdr/data/postgresql.conf[下記の修正を加える]
listen_addresses = '*'shared_preload_libraries = 'bdr'
wal_level = 'logical'
track_commit_timestamp = on
max_connections = 100
max_wal_senders = 10
max_replication_slots = 10
# Make sure there are enough background worker slots for BDR to run
max_worker_processes = 10
log_line_prefix = '%m d=%d p=%p a=%a%q '# These aren't required, but are useful for diagnosing problems#log_error_verbosity = verbose#log_min_messages = debug1# Useful options for playing with conflicts#bdr.default_apply_delay=2000 # milliseconds#bdr.log_conflicts_to_table=on

9. B DR のクライアント認証設定(p g_h b a.co n f)
B DR のクライアント認証設定(p g_h b a.co n f)をします。

$ vi /var/lib/pgsql/2ndquadrant_bdr/data/pg_hba.conf[下記の修正を加える]
host
all
all
192.168.0.10/32
host
all
all
192.168.0.12/32
local replication postgres
trust
host
replication postgres
0.0.0.0/0
trust
host
replication postgres::1/128
trust

trust
trust

10. Po stgreSQ L 起動
設定が環境しましたら Po stgreSQ L を起動します。
起動時に B DR のバックグランドワーカの登録メッセージが出力される事を確認します。

$ pg_ctl start
サーバは起動中です。

-bash-4.2$ < 2016-10-19 01:47:01.891 JST >LOG: バックグラウンドワーカ"bdr supervisor"を登録しています< 2016-10-19 01:47:02.041 JST >LOG: ログ出力をログ収集プロセスにリダイレクトし ています< 2016-10-19 01:47:02.041 JST >ヒント: ここからのログ出力はディレクトリ"pg_log"に現れます。

以下のコマンドでPo stgreSQ L の起動状況を確認します。

$ pg_ctl status
pg_ctl: サーバが動作中です(PID: 10948)/usr/pgsql-9.4/bin/postgres

11. データベース作成
Page 238 of 285

PostgreSQL Enterprise Consortium

B DR の動作確認用のデータベースを作成します。

$ createdb bdrtest

以下のコマンドで作成したデータベースへの接続を確認します。

$ psql bdrtest
psql (9.4.9)"help" でヘルプを表示します.
bdrtest=# \q-bash-4.2$

12. B DR モジュール登録
B DR に必要な拡張モジュールを登録します。

$ psql -U postgres bdrtest=# CREATE EXTENSION btree_gist;=# CREATE EXTENSION bdr;

以下のコマンドでインストール済みの拡張モジュールを確認します。

=# \dx
インストール済みの拡張の一覧
名前| バージョン | スキーマ |説明------------+------------+------------+----------------------------------------------bdr| 1.0.1.0| pg_catalog | Bi-directional replication for PostgreSQL
btree_gist | 1.0| public| support for indexing common datatypes in GiST
plpgsql| 1.0| pg_catalog | PL/pgSQL procedural language(3 行)

13. B DR グループ作成
n o de1 でB DR グループを作成するため、「 b dr.b dr_gro u p _create 」関数を実行します。

$ psql -U postgres bdrtest=# SELECT bdr.bdr_group_create(
local_node_name := 'node1',
node_external_dsn := 'host=node1 port=5432 dbname=bdrtest');

グループが作成されたことを確認するため、「 b dr.b dr_n o de_jo in _w ait_fo r_ready」関数を実行します。

=# SELECT bdr.bdr_node_join_wait_for_ready();bdr_node_join_wait_for_ready-----------------------------(1 行)

14. ノード登録
n o de2 でノード登録するために、「 b dr.b dr_gro u p _jo in 」関数を実行します。

Page 239 of 285

PostgreSQL Enterprise Consortium$ psql -U postgres bdrtest=# SELECT bdr.bdr_group_join(
local_node_name := 'node2',
node_external_dsn := 'host=node2 port=5432 dbname=bdrtest',
join_using_dsn := 'host=node1 port=5432 dbname=bdrtest');

ノードが登録されたことを確認するため、「 b dr.b dr_n o de_jo in _w ait_fo r_ready」関数を実行します。

=# SELECT bdr.bdr_node_join_wait_for_ready();bdr_node_join_wait_for_ready-----------------------------(1 行)

15. ノード状態の確認
ノードの状態を「 b dr.b dr_n o des 」テーブルの情報から確認します。

$ psql -U postgres bdrtest=# \x=# SELECT * FROM bdr.bdr_nodes;-[ RECORD 1 ]------+-----------------------------------node_sysid| 6369044607312522907
node_timeline| 1
node_dboid| 16385
node_status| r
node_name| node1
node_local_dsn| host=node1 port=5432 dbname=bdrtest
node_init_from_dsn |node_read_only| f-[ RECORD 2 ]------+-----------------------------------node_sysid| 6369045470046586386
node_timeline| 1
node_dboid| 16385
node_status| r
node_name| node2
node_local_dsn| host=node2 port=5432 dbname=bdrtest
node_init_from_dsn | host=node1 port=5432 dbname=bdrtest
node_read_only| f

16. 簡易動作検証(n o de1)n o de1 でB DR の簡易動作検証を実施します。
n o de1 で作成されたテーブルとデータがn o de2 にレプリケーションされていることを確認します。

$ psql -U postgres bdrtest=# CREATE TABLE t1bdr (c1 INT, PRIMARY KEY (c1));=# INSERT INTO t1bdr VALUES (1);=# INSERT INTO t1bdr VALUES (2);17. 簡易動作検証(n o de2)n o de2 でB DR の簡易動作検証の結果を確認します。
n o de1 と同じテーブルとデータが表示できていれば、構築した B DR 環境に問題はありません。
n o de1 と同様の簡易動作検証をn o de2 からも実施して下さい。

Page 240 of 285

PostgreSQL Enterprise Consortium$ psql -U postgres bdrtest=# SELECT * FROM t1bdr;
c1
---1
2
(2 行)
=# INSERT INTO t1bdr VALUES (3);7.5. BDR動作検証
7.5.1. ノ ー ドの 追加・ 切り離し
7.5.1.1. 検証の目的
ノードの追加/切り離しをオンライン(DB 停止)なしで実行できるか否かを確認します。

7.5.1.2. 検証内容
本検証では、2 台で構成された B DR クラスタに対して、下記を実施しました。

1. ノード切り離し
2. ノード追加「ノード切り離し」および「ノード追加」時に他ノード(上図のn o de1)にトランザクションを実行し、トランザクションにエラーが発生するか否かを確認しました。

7.5.1.3. 検証環境
こちらの環境 で検証を実施しました。

7.5.1.4. 検証手順
7.5.1.4.1. ノ ード 切 り 離 し

1. B DR の各ノード状態確認
B DR の各ノード間のレプリケーションが正常に動作していることを「 b dr.b dr_n o des 」テーブルを用いて確認します。

Page 241 of 285

PostgreSQL Enterprise Consortium(node1のデータベースに接続)=# SELECT node_name, node_local_dsn, node_status, node_init_from_dsn FROM bdr.bdr_nodes;-[ RECORD 1 ]------+-----------------------------------node_name| node1
node_local_dsn| host=node1 port=5432 dbname=bdrtest
node_status| r<-- r(正常)であることを確認
node_init_from_dsn |-[ RECORD 2 ]------+-----------------------------------node_name| node2
node_local_dsn| host=node2 port=5432 dbname=bdrtest
node_status| r<-- r(正常)であることを確認
node_init_from_dsn | host=node1 port=5432 dbname=bdrtest <-- node1から作成されていることを確認

2. 検証用テーブルとデータの作成
p gb eb ch を利用して、検証時に利用するテーブルとデータを作成します。

(node1にて実施)$ pgbench -i -s 10 bdrtest

3. トランザクション実行
p gb en ch を利用して、ノード1 にトランザクションを継続的に実行します。 ノード2 を切り離した発生した場合に、ノード1 に対して実行したトランザクショ
ンにエラーが発生するか否かを確認します。

$ pgbench -h node1 -c 10 -t 100000 bdrtest
starting vacuum...end.

4. ノード切り離し
ノード2 を切り離しするため、「 b dr.b dr_p art_b y_n o de_n ames 」関数を実行します。

(node1のデータベースに接続)=# SELECT bdr.bdr_part_by_node_names(ARRAY['node2']);bdr_part_by_node_names-----------------------(1 row)

5. ノードの切り離し結果確認
ノードの切り離し結果を「 b dr.b dr_n o des 」テーブルを用いて確認します。

(node1のデータベースに接続)=# SELECT node_name, node_local_dsn, node_status, node_init_from_dsn FROM bdr.bdr_nodes;-[ RECORD 1 ]------+-----------------------------------node_name| node1
node_local_dsn| host=node1 port=5432 dbname=bdrtest
node_status| r
node_init_from_dsn |-[ RECORD 2 ]------+-----------------------------------node_name| node2
node_local_dsn| host=node2 port=5432 dbname=bdrtest
node_status| k<-- k(削除)であることを確認
node_init_from_dsn | host=node1 port=5432 dbname=bdrtest

6. システムカタログの残データ削除

Page 242 of 285

PostgreSQL Enterprise Consortium
本手順は推奨される手順ではありませんが、削除状態のノードが「 b dr.b dr_n o des 」テーブルに残っていると削除したノードの追加が実行できないため暫定的な対処です。 B DR のIssu es で「 b dr.b dr_co n n ectio n s 」のデータも削除する事が提案されていたので、こちらも暫定的な手順ですが実行します。 詳細は下記をご参照下さい。

h ttp s://gith u b .co m/2n dQ u adran t/b dr/issu es/97
h ttp s://gith u b .co m/2n dQ u adran t/b dr/issu es/126(node1のデータベースに接続)=# DELETE FROM bdr.bdr_connections USING bdr.bdr_nodes WHERE node_status = 'k' AND(node_sysid, node_timeline, node_dboid) = (conn_sysid, conn_timeline, conn_dboid);=# DELETE FROM bdr.bdr_nodes where node_status = 'k';

7. ノードの切り離し結果確認
ノードの切り離し結果を「 b dr.b dr_n o des 」テーブルを用いて確認します。

(node1のデータベースに接続)=# SELECT node_name, node_local_dsn, node_status, node_init_from_dsn FROM bdr.bdr_nodes;-[ RECORD 1 ]------+-----------------------------------node_name| node1
node_local_dsn| host=node1 port=5432 dbname=bdrtest
node_status| r
node_init_from_dsn |8. B DR の無効化
ノード2 のデータベースから「 b dr.remo ve_b dr_fro m_lo cal_n o de 」関数を用いて B DR を削除し、B DR 拡張機能を削除します。

(node2のデータベースに接続)=# SELECT bdr.remove_bdr_from_local_node(true);WARNING: forcing deletion of possibly active BDR node
NOTICE: removing BDR from node
NOTICE: BDR removed from this node. You can now DROP EXTENSION bdr and,if this is the last BDR node on this PostgreSQL instance, remove bdr fromshared_preload_libraries.
remove_bdr_from_local_node---------------------------=# DROP EXTENSION bdr;
DROP EXTENSION

9. トランザクション状態実行状態確認
手順1 で実行した p gb en ch にエラーが発生してないことを確認します。本検証ではエラーは発生しませんでした。

7.5.1.4.2. ノ ード 追 加
切り離した B DR ノードを再度追加する場合、既存ノードのデータベースと追加するノードのデータベースのスキーマおよびデータを同期させる必要があります。 ノード間のデー
タコピーには、論理コピーと物理コピーの2 つの手法があります。

h ttp ://b dr-p ro ject.o rg/do cs/stab le/fu n ctio n s-n o de-mgmt.h tml#FU NCTIO N-B DR-G RO U P-JO IN
表 7.6 コピー取得方法
項番1

コ ピー取得b dr.b dr_gro u p _jo in 関

説明

備考

ユーザが指定したノード内データベースのスキーマとデータダンプを取得

p g_du mp コマンドに相当

ユーザが指定したノード上の全てのデータベースのコピーを取得

p g_b aseb acku p コマンド数実行

2

b dr_in it_co p yコマンドに相当■ 論理コピーによるノード追加

Page 243 of 285

PostgreSQL Enterprise Consortium

1. トランザクション実行
p gb en ch を利用して、ノード1 にトランザクションを継続的に実行します。 ノード2 に復旧する際に、ノード1 に対して実行したトランザクションの停止が必要か否かを確認します。

$ pgbench -h node1 -c 10 -t 100000 bdrtest
starting vacuum...end.

2. ノード2 のPo stgreSQ L 起動
ノード2 上で動作するPo stgreSQ L を起動させます。

$ pg_ctl start

3. データベース削除
B DR で利用したデータベースを削除します。

(node2のデータベースに接続)=# DROP DATABASE bdrtest ;DROP DATABASE※ 接続が残っており、削除できない場合はPostgreSQLを再起動

4. データベースの再作成
B DR で利用するデータべースを再度作成します。

(node2のデータベースに接続)=# CREATE DATABASE bdrtest;

5. B DR 有効化
無効化した B DR を再度有効化します。

(node2のデータベースに接続)=# CREATE EXTENSION btree_gist;
CREATE EXTENSION=# CREATE EXTENSION bdr;
CREATE EXTENSION

6. ノードの追加
ノードを追加(復旧)させるため、「 b dr.b dr_gro u p _jo in 」関数を実行します。

(node2のデータベースに接続)=# SELECT bdr.bdr_group_join(
local_node_name := 'node2',
node_external_dsn := 'host=node2 port=5432 dbname=bdrtest',
join_using_dsn := 'host=node1 port=5432 dbname=bdrtest');
bdr_group_join---------------(1 行)

Page 244 of 285

PostgreSQL Enterprise Consortium

7. ノード追加の確認待ち
ノードが追加されたことを確認するため、「 b dr.b dr_n o de_jo in _w ait_fo r_ready」関数を実行します。

(node2のデータベースに接続)=# SELECT bdr.bdr_node_join_wait_for_ready();※ トランザクションが実行中の場合、上記関数の結果が戻りません。

n o de1 のPo stgreSQ L ログファイルに下記メッセージが出力された後、復旧の処理が開始されません。

(node1のログメッセージ抜粋)
LOG: logical decoding found initial starting point at 0/BB399BF0
DETAIL: 10 transactions need to finish.

p gb en ch コマンドで実行中のトランザクションを停止すると、下記ログメッセージが出力され、復旧処理が開始されます。

(node1のログメッセージ抜粋)
LOG: logical decoding found consistent point at 0/B2895648
DETAIL: There are no running transactions.
LOG: exported logical decoding snapshot: "00046FE0-1" with 0 transaction IDs

8. B DR の各ノード状態確認
ノード2 が追加されたことを「 b dr.b dr_n o des 」テーブルの情報から確認します。

(node1のデータベースに接続)=# SELECT node_name, node_local_dsn, node_status, node_init_from_dsn FROM bdr.bdr_nodes;-[ RECORD 1 ]------+-----------------------------------node_name| node1
node_local_dsn| host=node1 port=5432 dbname=bdrtest
node_status| r<-- r(正常)であることを確認
node_init_from_dsn |-[ RECORD 2 ]------+-----------------------------------node_name| node2
node_local_dsn| host=node2 port=5432 dbname=bdrtest
node_status| r<-- r(正常)であることを確認
node_init_from_dsn | host=node1 port=5432 dbname=bdrtest<-- node1から作成されていることを確認■ 物理コピーによるノード追加
1. トランザクション実行
p gb en ch を利用して、ノード1 にトランザクションを継続的に実行します。 ノード2 に追加する際に、ノード1 に対して実行したトランザクションの停止が必要か否かを確認します。

$ pgbench -h node1 -c 10 -t 100000 bdrtest
starting vacuum...end.

2. ノード2 のPo stgreSQ L 停止確認
ノード2 上で動作するPo stgreSQ L が停止していることを確認します。

Page 245 of 285

PostgreSQL Enterprise Consortium(node2にて実施)$ pg_ctl status
pg_ctl: no server running

3. 物理コピーの取得
ノード2 上で「 b dr_in it_co p y」コマンドを実行し、ノード1 上のコピーを取得します。

(node2にて実施)$ rm -rf $PGDATA/*$ bdr_init_copy -D $PGDATA -n node2 -h node1 -p 5432 -d bdrtest--local-host=node2 --local-port=5432 --local-dbname=bdrtest
bdr_init_copy: starting ...
Getting remote server identification ...
Detected 1 BDR database(s) on remote server
Updating BDR configuration on the remote node:bdrtest: creating replication slot ...
bdrtest: creating node entry for local node ...
Creating base backup of the remote node...
50357/50357 kB (100%), 1/1 tablespace
Creating restore point on remote node ...
Bringing local node to the restore point ...
トランザクションログをリセットします。

Initializing BDR on the local node:bdrtest: adding the database to BDR cluster ...
All donen o de1 のPo stgreSQ L ログファイルに下記メッセージが出力された後、追加の処理が開始されません。

(node1のログメッセージ抜粋）
LOG: logical decoding found initial starting point at 0/BB399BF0
DETAIL: 10 transactions need to finish.

p gb en ch コマンドで実行中のトランザクションを停止すると、下記ログメッセージが出力され、復旧処理が開始されます。

(node1のログメッセージ抜粋
LOG: logical decoding found consistent point at 0/B2895648
DETAIL: There are no running transactions.
STATEMENT: SELECT
pg_create_logical_replication_slot('bdr_25434_6369931070716042622_2_25434__', 'bdr');

4. B DR の各ノード状態確認
ノード2 が追加されたことを「 b dr.b dr_n o des 」テーブルの情報から確認します。

Page 246 of 285

PostgreSQL Enterprise Consortium(node1のデータベースに接続)=# SELECT node_name, node_local_dsn, node_status, node_init_from_dsn FROM bdr.bdr_nodes;-[ RECORD 1 ]------+-----------------------------------node_name| node1
node_local_dsn| host=node1 port=5432 dbname=bdrtest
node_status| r<-- r(正常)であることを確認
node_init_from_dsn |-[ RECORD 2 ]------+-----------------------------------node_name| node2
node_local_dsn| host=node2 port=5432 dbname=bdrtest
node_status| r<-- r(正常)であることを確認
node_init_from_dsn | host=node1 port=5432 dbname=bdrtest<-- node1から作成されていることを確認

7.5.1.5. 検証結果
切り離したノードの情報がシステムカタログに残ってしまうため、データ操作が禁止されているシステムカタログのデータ削除が必要でした。
システムカタログのデータ操作は禁止されていますが、切り離したノードの情報がシステムカタログに残っている場合、 ノード追加時に下記のメッセージが出力され、ノードが追加が実施できないため、本検証では暫定対処としてシステムカタログのデータ削除を実施しております。

ERROR: System identification mismatch between connection and slot
詳細: Connection for bdr (6369029438929838565,1,16385,) resulted in slot on node bdr(6369034270871134883,2,16385,) instead of expected node
LOG: ワーカプロセス: bdr db: bdrtest (PID 17232)は終了コード1で終了しました

ノード追加にはプロセスの再起動は必要ありませんが、トランザクションの停止が必要でした。

7.5.2. グロー バル シ ー ケン ス設定
7.5.2.1. 検証の目的
B DR に実装されたグローバルシーケンスの利用方法について確認します。 グルーバルシーケンスを利用することでノード毎に払い出されるシーケンス番号を独立させ、ノード間のシーケンス番号の競合を防ぐことができるか否かを確認します。

7.5.2.2. 検証環境
こちらの環境 で検証を実施しました。

7.5.2.3. 検証手順
1. 初期状態
B DR の各ノード間のレプリケーションが正常に動作していることを「 b dr.b dr_n o des 」テーブルを用いて確認します。

(node1のデータベースに接続)=# SELECT node_name, node_local_dsn, node_status, node_init_from_dsn FROM bdr.bdr_nodes;-[ RECORD 1 ]------+-----------------------------------node_name| node1
node_local_dsn| host=node1 port=5432 dbname=bdrtest
node_status| r<-- r(正常)であることを確認
node_init_from_dsn |-[ RECORD 2 ]------+-----------------------------------node_name| node2
node_local_dsn| host=node2 port=5432 dbname=bdrtest
node_status| r<-- r(正常)であることを確認
node_init_from_dsn | host=node1 port=5432 dbname=bdrtest <-- node1から作成されていることを確認

2. グローバルシーケンス動作確認
n o de1 のデータベースに接続し、serial 型を列に持つ gstest テーブルを作成します。 serial 型を持つテーブルを作成すると、自動的にシーケンスが作成

Page 247 of 285

PostgreSQL Enterprise Consortiumされます。 グローバルシーケンス作成のため、テーブル作成時に「 SET LO CAL defau lt_sequ en ceam = 'b dr'; 」を指定する必要があります。

(node1のデータベースに接続)=#
BEGIN;SET LOCAL default_sequenceam = 'bdr';
CREATE TABLE gstest (id serial primary key,
hogehoge text);
COMMIT;<-- グローバルシーケンス作成用の設定

参考. 上記DDL実行時に node1に出力されるログメッセージ

LOG: DDL LOCK TRACE: attempting to acquire in mode <ddl_lock> for (bdr (xxxxxxxx))-------STATEMENT: CREATE TABLE gstest (id serial primary key,
hogehoge text);
LOG: DDL LOCK TRACE: attempting to acquire in mode <write_lock> (upgrading from
<ddl_lock>) for (bdr (xxxxxxxxx,))>STATEMENT: CREATE TABLE gstest (id serial primary key,
hogehoge text);--------

3. グルーバルシーケンス作成確認
n o de1 およびn o de2 のデータベースに接続し、グローバルシーケンスが作成されたことを確認します。

(node1のデータベースに接続)=# \dsList of relations-[ RECORD 1 ]--------Schema | public
Name | gstest_id_seq
Type | sequence
Owner | postgres(node2のデータベースに接続)=# \dsList of relations-[ RECORD 1 ]--------Schema | public
Name | gstest_id_seq
Type | sequence
Owner | postgres※ 下記のSQ L で詳細情報(p g_class の情報)を確認可能です。

=# SELECT * FROM pg_class
INNER JOIN pg_seqam ON (pg_class.relam = pg_seqam.oid)
WHERE pg_seqam.seqamname = 'bdr' AND relkind = 'S';

4. グローバルシーケンス利用
n o de1 のデータベースに接続し、1 件のデータを投入します。

(node1のデータベースに接続)=# INSERT INTO gstest(hogehoge) VALUES ('test1');
INSERT 0 1

n o de2 のデータベースに接続し、1 件のデータを投入します。

Page 248 of 285

PostgreSQL Enterprise Consortium(node2のデータベースに接続)=# INSERT INTO gstest(hogehoge) VALUES ('test2');
INSERT 0 1

5. シーケンス値の競合有無確認
n o de1 およびn o de2 のシーケンスの値に競合が発生していないことを確認します。 本検証では、n o de1 に 2 が割り当てられ、n o de2 には100001 が割り当てられました。

(node1のデータベースに接続)=# SELECT * FROM gstest;
id | hogehoge--------+---------2 | test1
100001 | test2(2 rows)<-- node1には2のシーケンスが割り当てられる<-- node2には100001のシーケンスが割り当てられる

6. グローバルシーケンス利用
n o de1 に 200,000 件のデータを投入し、シーケンスの値が100,000 を超えた場合、n o de2 の100,001 と競合しないか否かを確認しました。

(node1において実施)$ cat /tmp/gs.sh#!/bin/sh
for i in `seq 1 200000`
do
psql -d bdrtest -c "INSERT into gstest(hogehoge) VALUES ('test${i}');"
done$ sh /tmp/gs.sh
INSERT 0 1[省略]

7. グローバルの状態確認
n o de1 のシーケンスが100,000 を利用したのち、n o de1 に割り振られた値を確認しました。

(node1のデータベースに接続)=# SELECT * FROM gstest WHERE 99999 <= id;
id | hogehoge--------+-----------99999 | test99997
100000 | test99998
100001 | test2
150001 | test99999
150002 | test100000
150003 | test100001
150004 | test100002
150005 | test100003
150006 | test100004-< 100,000を超えると、node1のシーケンスが150,001に割り振られる。

7.5.2.4. 検証結果
グローバルシーケンスを用いることでノード間のシーケンス番号の競合を抑止できることが確認できました。 グルーバルシーケンスについてはいくつかの制限事項がマニュアルに記載されておりますので、注意して下さい。

1. 1 （デフォルト値）のINCREMENTだけがサポートされています。

Page 249 of 285

PostgreSQL Enterprise Consortium

2. MINVALU E とMAXVALU E はデフォルトでロックされており、変更することはできません。
3. CACHE 指令はサポートされていません。
h ttp ://b dr-p ro ject.o rg/do cs/1.0/glo b al-sequ en ce-limitatio n s.h tml

7.5.3. 選択的レプリ ケー シ ョ ン
選択的レプリケーションについて記載します。

7.5.3.1. 検証目的
テーブル単位での選択的レプリケーションの可否を確認します。 選択的レプリケーションの可否は互いのデータベース間での任意のテーブルのデータ状態を元に判断します。
また、選択的レプリケーションを実現する際の変更手順を明確にする事を目的とします。

7.5.3.2. 検証内容

Rep licatio n Set : レプリケーション対象とするテーブルの論理集合を表し、ノード毎に設定可能
h ttp ://b dr-p ro ject.o rg/do cs/stab le/rep licatio n -sets.h tml
選択的レプリケーション検証内容
双方向レプリケーションの動作確認
選択的レプリケーション(n o de1)の動作確認
選択的レプリケーション(n o de2)の動作確認

7.5.3.3. 検証環境
こちらの環境 で検証を実施しました。

7.5.3.4. 検証手順
1. 選択的レプリケーション環境構築
選択的レプリケーションの動作確認の為に、下記の検証環境を構築します。

(1)データベース作成
テストを実施する為のデータベースを作成します。

(node1,node2において実施)=# CREATE DATABASE rep_test;(2)B DR の有効化
作成したデータベース上でB DR 機能の有効化を実施します。

(node1,node2のデータベースにおいて実施)=# CREATE EXTENSION btree_gist;=# CREATE EXTENSION bdr;

Page 250 of 285

PostgreSQL Enterprise Consortium(3)B DR グループの作成
B DR ノードのクラスタに「 b dr.b dr_gro u p _create 」を利用して最初のノードを作成します。

(node1のデータベースに接続)=# SELECT bdr.bdr_group_create(
local_node_name := 'node1',
node_external_dsn := 'host=node1 port=5432 dbname=rep_test',
replication_sets:= ARRAY['default','node1']);に node1を追加※ replications_ret(4)ノード登録(n o de2)
既存のB DR ノードのクラスタに「 b dr.b dr_gro u p _jo in 」を利用してノードを登録します。 これにより、全てのノード間でレプリケーションが開始されます。

(node2のデータベースに接続)=# SELECT bdr.bdr_group_join(
local_node_name := 'node2',
node_external_dsn := 'host=node2 port=5432 dbname=rep_test',
join_using_dsn := 'host=node1 port=5432 dbname=rep_test',
replication_sets:= ARRAY['default','node2']);に node2を追加※ replications_ret(5)ノードの状態確認
B DR グループ内のノードのメンバシップを「 b dr.b dr_n o des 」を利用して確認します。

(node1のデータベースに接続)=# SELECT node_name, node_local_dsn, node_status, node_init_from_dsn FROM bdr.bdr_nodes;-[ RECORD 1 ]------+------------------------------------node_name| node1
node_local_dsn| host=node1 port=5432 dbname=rep_test
node_status| r<-- r(正常)であることを確認
node_init_from_dsn |-[ RECORD 2 ]------+------------------------------------node_name| node2
node_local_dsn| host=node2 port=5432 dbname=rep_test
node_status| r<-- r(正常)であることを確認
node_init_from_dsn | host=node1 port=5432 dbname=rep_test<-- node1から作成されていることを確
認(6)レプリケーションセットの確認
指定したノードのレプリケーションセットを「 b dr.co n n ectio n _get_rep licatio n _sets 」を利用して確認します。

(node1のデータベースに接続)=# SELECT bdr.connection_get_replication_sets('node1');-[ RECORD 1 ]-------------------+---------------connection_get_replication_sets | {default,node1}<-- node1が追加されていること=# SELECT bdr.connection_get_replication_sets('node2');-[ RECORD 1 ]-------------------+---------------connection_get_replication_sets | {default,node2}<-- node2が追加されていること

2. 双方向レプリケーション対象テーブルの動作確認
まずは、通常の双方向レプリケーション動作を確認します。 片方のテーブルにデータを挿入した時にもう一方のテーブルにもデータが挿入されています。

(1)テーブル(all_rep )作成

Page 251 of 285

PostgreSQL Enterprise Consortium
テストを実施する為のテーブル(all_rep )を作成します。

(node1のデータベースに接続)=# CREATE TABLE all_rep(id int, PRIMARY KEY(id));
CREATE TABLE(2)レプリケーションセット確認
指定したテーブルのレプリケーションセットを「 b dr.co n n ectio n _get_rep licatio n _sets 」を利用して確認します。

(node1のデータベースに接続)=# SELECT bdr.table_get_replication_sets('all_rep');-[ RECORD 1 ]--------------+-------------table_get_replication_sets | {default,all}<-- defaultが含まれていること(3)データ追加
n o de1 のテーブルにデータを挿入します。

(node1のデータベースに接続)=# INSERT INTO all_rep VALUES (1);
INSERT 0 1(4)レプリケーション確認
n o de2 のテーブルにもデータがレプリケーションされている事を確認します。

(node2のデータベースに接続)=# SELECT id FROM all_rep;
id---1(1 row)<-- データがレプリケーションされていること

3. レプリケーション対象外テーブルの作成(n o de1 のみ)
次に、選択的レプリケーション動作を確認します。 指定したテーブルではレプリケーション動作が行われなくなりますので、任意のテーブルのみレプリケーショ
ンさせる事が出来ます。

(1)テーブル(n o de1_o n ly)の作成
テストを実施する為のテーブル(n o de1_o n ly)を作成します。

(node1のデータベースに接続)=# CREATE TABLE node1_only(id int, PRIMARY KEY(id));
CREATE TABLE(2)レプリケーションセット変更
指定したテーブルのレプリケーションセットを「 b dr.tab le_set_rep licatio n _sets 」を利用して 'n o de1' に設定し、レプリケーション対象を'n o de1' のみにします。 設定したレプリケーションセットを確認するには、「 b dr.tab le_get_rep licatio n _sets 」を利用します。

Page 252 of 285

PostgreSQL Enterprise Consortium(node1のデータベースに接続)=# SELECT bdr.table_set_replication_sets('node1_only', ARRAY['node1']);-[ RECORD 1 ]--------------+table_set_replication_sets |=# SELECT bdr.table_get_replication_sets('node1_only');-[ RECORD 1 ]--------------+-----------table_get_replication_sets | {node1,all}<-- node1が追加されていること(3)データ追加
n o de1 のテーブルにデータを挿入します。

(node1のデータベースに接続)=# INSERT INTO node1_only VALUES (1);
INSERT 0 1(4)レプリケーション確認
n o de2 のテーブルにはデータがレプリケーションされていない事を確認します。

(node2のデータベースに接続)=# SELECT id FROM node1_only;
id---(0 行)<-- レプリケーションされていないこと

4. レプリケーション対象外テーブルの作成(n o de2 のみ)
同様に、もう一方からの選択的レプリケーション動作も確認します。

(1)テーブル(n o de2_o n ly)の作成
テストを実施する為のテーブル(n o de2_o n ly)を作成します。

(node2のデータベースに接続)=# CREATE TABLE node2_only(id int, PRIMARY KEY(id));
CREATE TABLE(2)レプリケーションセット変更
指定したテーブルのレプリケーションセットを「 b dr.tab le_set_rep licatio n _sets 」を利用して 'n o de2' に設定し、レプリケーション対象を'n o de2' のみにします。 設定したレプリケーションセットを確認するには、「 b dr.tab le_get_rep licatio n _sets 」を利用します。

(node2のデータベースに接続)=# SELECT bdr.table_set_replication_sets('node2_only', ARRAY['node2']);-[ RECORD 1 ]--------------+table_set_replication_sets |=# SELECT bdr.table_get_replication_sets('node2_only');-[ RECORD 1 ]--------------+-----------table_get_replication_sets | {node2,all}<-- node2が追加されていること(3)データ追加
n o de2 のテーブルにデータを挿入します。

Page 253 of 285

PostgreSQL Enterprise Consortium(node2のデータベースに接続)=# INSERT INTO node2_only VALUES (1);
INSERT 0 1(4)レプリケーション確認
n o de1 のテーブルにはデータがレプリケーションされていない事を確認します。

(node1のデータベースに接続)=# SELECT id FROM node2_only;
id---(0 行)<-- レプリケーションされていないこと

7.5.3.5. 検証結果
今回の検証結果では、レプリケーションセットに任意のノードを指定する事で選択的レプリケーションが実現される事が確認出来ました。 また、選択的レプリケーション実現の為の設定変更手順についても確認出来ました。
ただし、レプリケーションセットにテーブルを追加する場合は、過去のデータまで反映しないので、手動で同期が必要です。
選択的レプリケーションの主な注意事項は下記になります。

h ttp ://b dr-p ro ject.o rg/do cs/1.0/rep licatio n -sets-co n cep ts.h tml
h ttp ://b dr-p ro ject.o rg/do cs/1.0/rep licatio n -sets-tab les.h tml
1. DDL はレプリケーションセットの設定に関係なく常にすべてのノードに影響します。
2. TRU NCATE は常にレプリケートされます。望ましくない場合は、DELETE を利用する必要があります。
3. レプリケーションセットの設定は、初期ノード追加(結合時)にはテーブルデータのコピーに影響を与えません。
4. レプリケーションセットにテーブルを追加した場合も以前のデータ内容はノードに同期されません。 通常、管理者は、テーブルをレプリケーションセットに追加した後、手動で同期する必要があります。

7.5.4. 更新処理競合時の 動作
7.5.4.1. 検証目的
B DR はマルチマスタ構成する各ノードに対して、参照処理と更新処理を実行することが可能です。 複数ノードに対して同時に更新処理が実施された場合、各ノードに対して実行された更新処理が競合する事象が発生する場合があります。

B DR 競合が発生した場合、最後の更新処理が適用されます(last_u p date_w in s)。また、競合結果はテーブル「 b dr.b dr_co n flict_h isto ry」で確認可能です。
「 b dr.b dr_co n flict_h isto ry」に格納される情報については下記をご参照下さい。

h ttp ://b dr-p ro ject.o rg/do cs/stab le/co n flicts-typ es.h tml
下記を明らかにするため検証を実施しました。

1. 更新処理が競合した場合の挙動
2. 競合発生時にシステムカタログに記録される情報
7.5.4.2. 検証内容

B DR 動作検証概要図

Page 254 of 285

PostgreSQL Enterprise Consortium
下表の競合発生時の動作を検証しました。
表 7.7 検証した競合のパターン
項番

分類

PRIMARY KEYまたはU NIQ U E 制約

1説明

2 つの操作が同じU NIQ U E KEYを持つ行に影響を及ぼす
行の競合を検証します。

2

外部キー制約

外部キー制約が定義されたテーブルにおいて、制約に違反するデータ削除によって引き起こされる競合を検証します。

3

排他制約

B DR では排他制約をサポートしていないために、排他制約において競合が発生した場合を検証します。

4グローバルなデータ

ノードのグローバル(Po stgreSQ L システム全体)の
データ (ロールなど)が異なる場合での競合を検証します。

5

ロックの競合とデッドロックの中断

B DR 適用プロセスとロックの競合について検証します。

6

その他

自動的に解決出来ないデータの相違が発生した場合に手動で調整を行う方法を検証します。

7.5.4.3. 検証環境
こちらの環境 で検証を実施しました。

7.5.4.4. 検証手順
1. 競合ログオプションの有効化確認
競合発生時にテーブル「 b dr.b dr_co n flict_h isto ry」に情報を記録するため、b dr.lo g_co n flicts_to _tab le パラメータがo n に設定されていることを確認します。
※ o ff になっていたら、p o stgresql.co n f に「 b dr.lo g_co n flicts_to _tab le= o n 」を追記し、再読み込みを実行して下さい。

n o de1/n o de2 のデータベースに接続し、パラメータを確認するために下記コマンドを実行します。

(node1にて実施)=# SHOW bdr.log_conflicts_to_table;
bdr.log_conflicts_to_table---------------------------on(1 行)(node2にて実施)=# SHOW bdr.log_conflicts_to_table;
bdr.log_conflicts_to_table---------------------------on(1 行)

2. レプリケーション反映時間の設定
ネットワーク遅延などの実際の環境を想定して検証を行うため、b dr.defau lt_ap p ly_delayパラメータを設定します。 このパラメータを設定する事でレプリケーションの反映を設定した時間(ミリ秒)遅らせる事が出来ます。
※ 今回の検証では、競合の発生を確認しやすくするために "2s"を設定しています。

n o de1/n o de2 のデータベースに接続し、パラメータを確認するために下記コマンドを実行します。

(node1にて実施)=# SHOW bdr.default_apply_delay;
bdr.default_apply_delay---------------------------2s(1 行)(node2にて実施)=# SHOW bdr.default_apply_delay;
bdr.default_apply_delay---------------------------2s(1 行)

3. B DR の各ノード状態確認
B DR の各ノード間のレプリケーションが正常に動作していることを「 b dr.b dr_n o des 」を利用して確認します。

Page 255 of 285

PostgreSQL Enterprise Consortium(node1にて実施)=# SELECT node_name, node_local_dsn, node_status, node_init_from_dsn FROM bdr.bdr_nodes;-[ RECORD 1 ]------+-----------------------------------node_name| node1
node_local_dsn| host=node1 port=5432 dbname=bdrtest
node_status| r
node_init_from_dsn |-[ RECORD 2 ]------+-----------------------------------node_name| node2
node_local_dsn| host=node2 port=5432 dbname=bdrtest
node_status| r
node_init_from_dsn | host=node1 port=5432 dbname=bdrtest<-- r(正常)であることを確認<-- r(正常)であることを確認<-- node1から作成されていることを確認

4. 検証用テーブルとデータの作成
p gb eb ch を利用して、検証時に利用するテーブルとデータを作成します。

(node1にて実施)$ pgbench -i -s 10 bdrtest$ psql -h node1 -d bdrtest=# INSERT INTO pgbench_accounts (aid, bid, abalance) VALUES (1000001, 1, 0);

7.5.4.4.1. PRIMARY KEYま た は UNIQUE制 約

1. INSERT vs INSERT■ 競合概要最も一般的な競合として、2 つの異なるノードのINSERTが同じPRIMARY KEYの値(または、単一のU NIQ E 制約の値)を持つデータを挿入するケースを検証しました。

■ 検証結果タイムスタンプに従い、最後にデータが作成された方が保持されました。

■ 競合発生手順
各ノードからそれぞれINSERTを実行し、INSERT vs INSERT の競合を発生させます。

(node1のデータベースへSQLを投入)=# INSERT INTO pgbench_accounts (aid, bid, abalance) VALUES (1000002, 2, 0);(node2のデータベースへSQLを投入)=# INSERT INTO pgbench_accounts (aid, bid, abalance) VALUES (1000002, 3, 0);

競合ログを「 b dr.b dr_co n flict_h isto ry」を利用して確認し、競合の発生を確認します。
※ 競合履歴テーブルでは競合ログが出力されている側のサーバをローカル、反対側をリモートとして表示します。 よって、それぞれのサーバで実行されたトランザクションをローカルトランザクション、リモートトランザクションとして説明します。

Page 256 of 285

PostgreSQL Enterprise Consortium(node1またはnode2の競合履歴テーブルに競合ログが出力)=# \x=# SELECT * FROM bdr.bdr_conflict_history ORDER BY conflict_id DESC LIMIT 1;-[ RECORD 1 ]------------+--------------------------------------------------conflict_id| 48
local_node_sysid| 6371561413984311673
local_conflict_xid| 3944663
local_conflict_lsn| 3/765949B8
local_conflict_time| 2017-01-25 10:36:03.787698+09
object_schema| public
object_name| pgbench_accounts
remote_node_sysid| 6371553825031594764
remote_txid| 3960113
remote_commit_time| 2017-01-25 10:32:48.772254+09
remote_commit_lsn| 2/CA1AD950
conflict_type| insert_insert
conflict_resolution| last_update_wins_keep_local
local_tuple| {"aid":1000002,"bid":3,"abalance":0,"filler":null}
remote_tuple| {"aid":1000002,"bid":2,"abalance":0,"filler":null}
local_tuple_xmin| 3944662
local_tuple_origin_sysid |error_message|
error_sqlstate|
error_querystring|
error_cursorpos|
error_detail|error_hint|
error_context|
error_columnname|
error_typename|
error_constraintname|
error_filename|
error_lineno|
error_funcname|

それぞれのテーブルの状態を確認し、競合発生時の処理を確認します。
※ 後にデータが作成されたローカルトランザクションが保持されています。

(node1のデータベースを確認)=# SELECT * FROM pgbench_accounts WHERE aid >= 1000001;
aid | bid | abalance | filler--------+-----+----------+-------1000001 | 1 |0 |1000002 | 3 |
0 |
(2 行)
(node2のデータベースを確認)=# SELECT * FROM pgbench_accounts WHERE aid >= 1000001;
aid | bid | abalance | filler--------+-----+----------+-------1000001 | 1 |0 |1000002 | 3 |
0 |
(2 行)
※ last_update_wins_keep_local : 最新のタイムスタンプであるローカル側の更新が適用

2. INSERT vs U PDATE■ 競合概要一つのノードでINSERTしたデータともう片方のノードでU PDATE したデータが同じPRIMARY KEYの値を持つケースを検証しました。

Page 257 of 285

PostgreSQL Enterprise Consortium■ 検証結果INSERT/U PDATE の競合が発生した場合、競合を解消するためオペレータ側でのデータ操作が必要となり、注意が必要です。
■ 競合発生手順
下記テーブルを初期状態とします。

(node1のデータベースを確認)=# SELECT * FROM pgbench_accounts WHERE aid >= 1000001;
aid | bid | abalance | filler--------+-----+----------+-------1000001 | 1 |
0 |
(1 行)
(node2のデータベースを確認)=# SELECT * FROM pgbench_accounts WHERE aid >= 1000001;
aid | bid | abalance | filler--------+-----+----------+-------1000001 | 1 |
0 |
(1 行)

各ノードからそれぞれINSERT/U PDATE を実行し、INSERT vs U PDATE の競合を発生させます。

(node1のデータベースへSQLを投入)=# INSERT INTO pgbench_accounts (aid, bid, abalance) VALUES (1000002, 4, 0);(node2のデータベースへSQLを投入)=# UPDATE pgbench_accounts SET aid = 1000002 WHERE aid = 1000001;

競合ログを「 b dr.b dr_co n flict_h isto ry」を利用して確認し、競合の発生を確認します。

Page 258 of 285

PostgreSQL Enterprise Consortium(node1またはnode2の競合履歴テーブルに競合ログが出力)=# \x=# SELECT * FROM bdr.bdr_conflict_history ORDER BY conflict_id DESC LIMIT 1;-[ RECORD 1 ]------------+--------------------------------------------------conflict_id| 49
local_node_sysid| 6371561413984311673
local_conflict_xid| 3944715
local_conflict_lsn| 3/81295CF8
local_conflict_time| 2017-01-25 10:36:46.780822+09
object_schema| public
object_name| pgbench_accounts
remote_node_sysid| 6371553825031594764
remote_txid| 3960133
remote_commit_time| 2017-01-25 10:33:31.640126+09
remote_commit_lsn| 2/D1CE6F00
conflict_type| insert_insert
conflict_resolution| last_update_wins_keep_local
local_tuple| {"aid":1000002,"bid":1,"abalance":0,"filler":null}
remote_tuple| {"aid":1000002,"bid":4,"abalance":0,"filler":null}
local_tuple_xmin| 3944714
local_tuple_origin_sysid |error_message|
error_sqlstate|
error_querystring|
error_cursorpos|
error_detail|error_hint|
error_context|
error_columnname|
error_typename|
error_constraintname|
error_filename|
error_lineno|
error_funcname|

それぞれのテーブルの状態を確認し、競合発生時の処理を確認します。

(node1のデータベースを確認)=# SELECT * FROM pgbench_accounts WHERE aid >= 1000001;
aid | bid | abalance | filler---------+-----+----------+-------1000001 | 1 |0 |1000002 | 4 |
0 |
(2 行)
(node2のデータベースを確認)=# SELECT * FROM pgbench_accounts WHERE aid >= 1000001;
aid | bid | abalance | filler---------+-----+----------+-------1000002 | 1 |
0 |
(1 行)

PRIMARY KEYのU PDATE で発生した競合でリモート側では一意制約違反のために適用プロセスがデータ反映できず、 ローカル側では論理レプリケー
ション中に接続が切断され、データの整合性がとれなくなる状態が確認されました。

Page 259 of 285

PostgreSQL Enterprise Consortium(node1のデータベースログ)
LOG: starting background worker process "bdr (6371553825031594764,1,43905,)->bdr(6371561413984311673,2,"
ERROR: duplicate key value violates unique constraint "pgbench_accounts_pkey"
DETAIL: Key (aid)=(1000002) already exists.
CONTEXT: apply UPDATE from remote relation public.pgbench_accounts in commit
1/2F279190, xid 3941360 commited at 2017-01-10 11:00:22.736985+09 (action #2) from node
(6371561413984311673,2,43865)
LOG: worker process: bdr (6371553825031594764,1,43905,)->bdr (6371561413984311673,2,(PID 21287) exited with exit code 1(node2のデータベースログ)
bdr (6371553825031594764,1,43905,):receive LOG: starting logical decoding for slot
"bdr_43865_6371553825031594764_1_43905__"bdr (6371553825031594764,1,43905,):receive DETAIL: streaming transactions committing
after 1/2F279190, reading WAL from 1/2F279068
bdr (6371553825031594764,1,43905,):receive LOG: logical decoding found consistent point
at 1/2F279068
bdr (6371553825031594764,1,43905,):receive DETAIL: There are no running transactions.
bdr (6371553825031594764,1,43905,):receive LOG: could not receive data from client:
Connection reset by peerbdr (6371553825031594764,1,43905,):receive LOG: unexpected EOF on standby connection※ 競合するタプルをローカル側から手動で削除するか、新しいリモートタプルと競合しなくなるように UPDATEする必要がある

上記状態では、ローカル側からのレプリケーションが実施されない状態が続くため、解消するために、ローカル側のデータをU PDATE します。

(node1のデータベースへSQLを投入)=# UPDATE pgbench_accounts SET aid = 1000003 WHERE aid = 1000002;=# SELECT * FROM pgbench_accounts WHERE aid >= 1000001;
aid | bid | abalance | filler---------+-----+----------+-------1000001 | 1 |0 |1000003 | 4 |
0 |
(2 行)
(node2のデータベースを確認)=# SELECT * FROM pgbench_accounts WHERE aid >= 1000001;
aid | bid | abalance | filler---------+-----+----------+-------1000002 | 1 |
0 |
(1 行)
※ 競合は解消され、次のデータからレプリケーションが始まるがデータの整合性で問題あり

3. U PDATE vs DELETE■ 競合概要一つのノードでU PDATE したデータともう片方のノードでDELETE したデータが同じPRIMARY KEYの値を持つケースを検証しました。

■ 検証結果U PDATE/DELETE が競合した場合、U PDATE が破棄されました。
■ 競合発生手順
下記テーブルを初期状態とします。

Page 260 of 285

PostgreSQL Enterprise Consortium(node1のデータベースを確認)=# SELECT * FROM pgbench_accounts WHERE aid >= 1000001;
aid | bid | abalance | filler---------+-----+----------+-------1000001 | 1 |
0 |
(1 行)
(node2のデータベースを確認)=# SELECT * FROM pgbench_accounts WHERE aid >= 1000001;
aid | bid | abalance | filler---------+-----+----------+-------1000001 | 1 |
0 |
(1 行)

各ノードからそれぞれU PDATE/DELETE を実行し、U PDATE vs DELETE の競合を発生させます。

(node1のデータベースに SQLを投入)=# UPDATE pgbench_accounts SET bid = 4 WHERE aid = 1000001;(node2のデータベースに SQLを投入)=# DELETE FROM pgbench_accounts WHERE aid = 1000001;

競合ログを「 b dr.b dr_co n flict_h isto ry」を利用して確認し、競合の発生を確認します。

(node1またはnode2の競合履歴テーブルに競合ログが出力)=# \x=# SELECT * FROM bdr.bdr_conflict_history ORDER BY conflict_id DESC LIMIT 1;-[ RECORD 1 ]------------+--------------------------------------------------conflict_id| 51
local_node_sysid| 6371561413984311673
local_conflict_xid| 0
local_conflict_lsn| 3/8BF7BA38
local_conflict_time| 2017-01-25 10:37:45.561678+09
object_schema| public
object_name| pgbench_accounts
remote_node_sysid| 6371553825031594764
remote_txid| 3960157
remote_commit_time| 2017-01-25 10:34:30.564936+09
remote_commit_lsn| 2/D9831CC0
conflict_type| update_delete
conflict_resolution| skip_change
local_tuple|
remote_tuple| {"aid":1000001,"bid":4,"abalance":0,"filler":null}
local_tuple_xmin|
local_tuple_origin_sysid |error_message|
error_sqlstate|
error_querystring|
error_cursorpos|
error_detail|error_hint|
error_context|
error_columnname|
error_typename|
error_constraintname|
error_filename|
error_lineno|
error_funcname|

Page 261 of 285

PostgreSQL Enterprise Consortium
それぞれのテーブルの状態を確認し、競合発生時の処理を確認します。
※ DELETE 後のU PDATE が破棄されています。

(node1のデータベースを確認)=# SELECT * FROM pgbench_accounts WHERE aid >= 1000001;
aid | bid | abalance | filler-----+-----+----------+-------(0 行)(node1のデータベースを確認)=# SELECT * FROM pgbench_accounts WHERE aid >= 1000001;
aid | bid | abalance | filler-----+-----+----------+-------(0 行)※ skip_change : 変更を無視し、破棄された

4. INSERT vs DELETE■ 競合概要一つのノードでINSERTしたデータがもう片方のノードでDELETE されたデータと同じPRIMARY KEYの値を持つケースを検証しました。

■ 検証結果DELETE の処理が破棄されました。競合発生の状態についてはシステムカタログから確認することができませんでした。
■ 競合発生手順
下記テーブルを初期状態とします。

(node1のデータベースを確認)=# SELECT * FROM pgbench_accounts WHERE aid >= 1000001;
aid | bid | abalance | filler---------+-----+----------+-------1000001 | 1 |
0 |
(1 行)
(node2のデータベースを確認)=# SELECT * FROM pgbench_accounts WHERE aid >= 1000001;
aid | bid | abalance | filler---------+-----+----------+-------1000001 | 1 |
0 |
(1 行)

各ノードからそれぞれINSERT/DELETE を実行し、INSERT vs DELETE の競合を発生させます。

(node1のデータベースに SQLを投入)=# INSERT INTO pgbench_accounts (aid, bid, abalance) VALUES (1000002, 5, 0);(node2のデータベースに SQLを投入)=# DELETE FROM pgbench_accounts WHERE aid = 1000002;

競合ログを「 b dr.b dr_co n flict_h isto ry」を利用して確認し、競合の発生を確認します。
※ 競合は確認出来ませんでした。

Page 262 of 285

PostgreSQL Enterprise Consortium(node1またはnode2の競合履歴テーブルに競合ログが出力されない)=# \x=# SELECT * FROM bdr.bdr_conflict_history ORDER BY conflict_id DESC LIMIT 1;

それぞれのテーブルの状態を確認します。

(node1のデータベースを確認)=# SELECT * FROM pgbench_accounts WHERE aid >= 1000001;
aid | bid | abalance | filler---------+-----+----------+-------1000001 | 1 |0 |1000002 | 5 |
0 |
(2 行)
(node2のデータベースを確認)=# SELECT * FROM pgbench_accounts WHERE aid >= 1000001;
aid | bid | abalance | filler---------+-----+----------+-------1000001 | 1 |0 |1000002 | 5 |
0 |
(2 行)

5. DELETE vs DELETE■ 競合概要2 つの異なるノードで実行された DELETE が同じPRIMARY KEYの値を持つデータを削除するケースを検証しました。
■ 検証結果DELETE/DELETE が競合した場合、片方のDELETE が無視され処理を完了しました。
■ 競合発生手順
下記テーブルを初期状態とします。

(node1のデータベースを確認)=# SELECT * FROM pgbench_accounts WHERE aid >= 1000001;
aid | bid | abalance | filler---------+-----+----------+-------1000001 | 1 |
0 |
(1 行)
(node2のデータベースを確認)=# SELECT * FROM pgbench_accounts WHERE aid >= 1000001;
aid | bid | abalance | filler---------+-----+----------+-------1000001 | 1 |
0 |
(1 行)

各ノードからそれぞれDELETE を実行し、DELETE vs DELETE の競合を発生させます。

(node1のデータベースに SQLを投入)=# DELETE FROM pgbench_accounts WHERE aid = 1000001;(node2のデータベースに SQLを投入)=# DELETE FROM pgbench_accounts WHERE aid = 1000001;

競合ログを「 b dr.b dr_co n flict_h isto ry」を利用して確認し、競合の発生を確認します。

Page 263 of 285

PostgreSQL Enterprise Consortium(node1またはnode2の競合履歴テーブルに競合ログが出力)=# \x=# SELECT * FROM bdr.bdr_conflict_history ORDER BY conflict_id DESC LIMIT 1;-[ RECORD 1 ]------------+--------------------------------------------------------conflict_id| 52
local_node_sysid| 6371561413984311673
local_conflict_xid| 0
local_conflict_lsn| 3/A193CA88
local_conflict_time| 2017-01-25 10:39:07.794699+09
object_schema| public
object_name| pgbench_accounts
remote_node_sysid| 6371553825031594764
remote_txid| 3960197
remote_commit_time| 2017-01-25 10:35:52.78243+09
remote_commit_lsn| 2/E8E9A388
conflict_type| delete_delete
conflict_resolution| skip_change
local_tuple|
remote_tuple| {"aid":1000001,"bid":null,"abalance":null,"filler":null}
local_tuple_xmin|
local_tuple_origin_sysid |error_message|
error_sqlstate|
error_querystring|
error_cursorpos|
error_detail|error_hint|
error_context|
error_columnname|
error_typename|
error_constraintname|
error_filename|
error_lineno|
error_funcname|

それぞれのテーブルの状態を確認し、競合発生時の処理を確認します。
※ ローカル側のノードからのDELETE が無視されています。

(node1のデータベースを確認)=# SELECT * FROM pgbench_accounts WHERE aid >= 1000001;
aid | bid | abalance | filler-----+-----+----------+-------(0 行)(node2のデータベースを確認)=# SELECT * FROM pgbench_accounts WHERE aid >= 1000001;
aid | bid | abalance | filler-----+-----+----------+-------(0 行)※ skip_change : 変更を無視し、破棄された

7.5.4.4.2. 外 部 キー制 約

1. 外部キー制約の競合■ 競合概要1 つのノードで外部キー制約が定義されたテーブルに対してデータを挿入し、上記データ挿入がもう片方のノードに反映される前に、 もう片方のノードにおいて外部キーの参照先であるテーブルのデータを削除することで、外部キー制約が定義されたテーブルへのデータ挿入と、外部キー参照先テーブルに対するデータ削除を競合させます。

Page 264 of 285

PostgreSQL Enterprise Consortium■ 検証結果外部キー制約に違反する処理が実施された場合でもB DR ではエラーにならず、データが外部キー制約違反の状態になりました。

■ 競合発生手順
下記の親子関係を持つテーブルを作成します。

(node1のデータベースに接続)=# CREATE TABLE parent(id integer primary key);=# CREATE TABLE child(id integer primary key, parent_id integer not null references
parent(id));=# INSERT INTO parent(id) VALUES (1), (2);=# INSERT INTO child(id, parent_id) VALUES (11, 1), (12, 2);

各ノードからそれぞれINSERT/DELETE を実行し、外部キー制約の競合を発生させます。
※ n o de1 からのINSERTはn o de2 では、子テーブルで参照されている親が存在せず、 n o de2 のDELETE はn o de1 で参照している親データを削除しようとしているので、それぞれ適用出来なくなります。

(node1のデータベースに SQLを投入)=# INSERT INTO child(id, parent_id) VALUES (21, 2);(node2のデータベースに SQLを投入)=# DELETE FROM child WHERE parent_id = 2;=# DELETE FROM parent WHERE id = 2;

それぞれのテーブルの状態を確認し、競合発生時の処理を確認します。

(node1のデータベースを確認)=# SELECT * FROM child;id | parent_id----+----------11 |
1
21 |
2
(2 行)
=# SELECT * FROM parent;
id
---1
(1 行)

(node2のデータベースを確認)=# SELECT * FROM child;id | parent_id----+----------11 |
1
21 |
2
(2 行)
=# SELECT * FROM parent;
id
---1
(1 行)
※ 外部キー制約違反のデータがchildテーブルに存在する。

7.5.4.4.3. 排 他 制 約

1. 排他制約の競合■ 競合概要排他な関係である2 つのデータを異なるノードから同時にテーブルに INSERTし、ノード間のデータに競合が発生するケースを検証します。

■ 検証結果一つのノードでINSERTされたデータをもう片方のノードに反映させる際に排他制約違反が発生し、ノード間の接続が切断されます。 競合を解消するた
めにはオペレータでのデータ操作が必要となるため注意が必要です。

■ 競合発生手順
下記の排他制約を持つテーブルを作成します。

Page 265 of 285

PostgreSQL Enterprise Consortium(node1のデータベースに SQLを投入)=# CREATE TABLE sample (aid integer primary key, range daterange, price integer);=# ALTER TABLE sample ADD EXCLUDE USING gist (price WITH =, range WITH &&);

各ノードからそれぞれINSERTを実行し、排他制約の競合を発生させます。
※ [2012-04-18 ～ 2012-04/20]の期間が競合しているために排他制約違反を起こしています。

(node1のデータベースに SQLを投入)=# INSERT INTO sample VALUES(1, '[2012-04-16, 2012-04-20]', 10000);(node2のデータベースに SQLを投入)=# INSERT INTO sample VALUES(2, '[2012-04-18, 2012-04-23]', 10000);(node1またはnode2の競合履歴テーブルに競合ログが出力されない)=# \x=# SELECT * FROM bdr.bdr_conflict_history ORDER BY conflict_id DESC LIMIT 1;

それぞれのテーブルの状態を確認し、競合発生時の処理を確認します。
※ 他のノードで実行されたデータ更新を反映する際に、制約違反が発生し、B DR によるノード間の接続が切断されます。

(node1のデータベースを確認)=# SELECT * FROM sample;
aid |
range
| price-----+-------------------------+------1 | [2012-04-16,2012-04-21) | 10000(1 行)(node2のデータベースを確認)=# SELECT * FROM sample;
aid |
range
| price-----+-------------------------+------2 | [2012-04-18,2012-04-24) | 10000(1 行)

Page 266 of 285

PostgreSQL Enterprise Consortium(node1のデータベースログ)
bdr (6371561413984311673,2,42879,):receive LOG: starting logical decoding for slot
"bdr_42892_6371561413984311673_2_42879__"bdr (6371561413984311673,2,42879,):receive DETAIL: streaming transactions committing
after 1/15D41640, reading WAL from 1/15D413B0
bdr (6371561413984311673,2,42879,):receive LOG: logical decoding found consistent point
at 1/15D413B0
bdr (6371561413984311673,2,42879,):receive DETAIL: There are no running transactions.
bdr (6371561413984311673,2,42879,):receive LOG: unexpected EOF on standby connection
LOG: starting background worker process "bdr (6371553825031594764,1,42892,)->bdr(6371561413984311673,2,"
ERROR: conflicting key value violates exclusion constraint "sample_price_range_excl"
DETAIL: Key (price, range)=(10000, [2012-04-18,2012-04-24)) conflicts with existing key(price, range)=(10000, [2012-04-16,2012-04-21)).
CONTEXT: apply INSERT from remote relation public.sample in commit 1/194FBC20, xid
3940666 commited at 2017-01-06 16:12:28.275376+09 (action #2) from node
(6371561413984311673,2,42879)
LOG: worker process: bdr (6371553825031594764,1,42892,)->bdr (6371561413984311673,2,(PID 27843) exited with exit code 1(node2のデータベースログ)
LOG: starting background worker process "bdr (6371561413984311673,2,42879,)->bdr(6371553825031594764,1,"
ERROR: conflicting key value violates exclusion constraint "sample_price_range_excl"
DETAIL: Key (price, range)=(10000, [2012-04-16,2012-04-21)) conflicts with existing key(price, range)=(10000, [2012-04-18,2012-04-24)).
CONTEXT: apply INSERT from remote relation public.sample in commit 1/15D41640, xid
3957701 commited at 2017-01-06 16:09:10.299493+09 (action #2) from node
(6371553825031594764,1,42892)
LOG: worker process: bdr (6371561413984311673,2,42879,)->bdr (6371553825031594764,1,(PID 29311) exited with exit code 1
bdr (6371553825031594764,1,42892,):receive LOG: starting logical decoding for slot
"bdr_42879_6371553825031594764_1_42892__"bdr (6371553825031594764,1,42892,):receive DETAIL: streaming transactions committing
after 1/194FB9D0, reading WAL from 1/194FB700
bdr (6371553825031594764,1,42892,):receive LOG: logical decoding found consistent point
at 1/194FB700
bdr (6371553825031594764,1,42892,):receive DETAIL: There are no running transactions.
bdr (6371553825031594764,1,42892,):receive LOG: unexpected EOF on standby connection※ 各ノードで制約違反が発生し、BDRによる接続が切断されてしまう。解消のためリモートタプルが競合するローカルタプルを削除または変更する

制約違反の状態を解消するために、両側のデータをU PDATE します。

(node1のデータベースに SQLを投入)=# UPDATE sample SET range = '[2012-04-11,2012-04-16]' WHERE aid = 1;
(node2のデータベースに SQLを投入)=# UPDATE sample SET range = '[2012-04-23,2012-04-29]' WHERE aid = 2;Page 267 of 285

PostgreSQL Enterprise Consortium(node1のデータベースを確認)=# SELECT * FROM sample;
aid |
range
| price-----+-------------------------+------1 | [2012-04-11,2012-04-17) | 10000
2 | [2012-04-23,2012-04-30) | 10000(2 行)(node2のデータベースを確認)=# SELECT * FROM sample;
aid |
range
| price-----+-------------------------+------2 | [2012-04-23,2012-04-30) | 10000
1 | [2012-04-11,2012-04-17) | 10000(2 行)

7.5.4.4.4. グ ロ ーバ ルな データ

1. グローバルなデータの競合■ 競合概要ロール(グローバルデータ )の情報がノード間で異なる状態で、他のノードに存在しないロールを利用した場合に発生する競合のケースを検証しました。

■ 検証結果レプリケーション先のノードに同名のロールが存在しない場合、エラーになります。 エラーを解消するには、オペレータ側での操作が必要となるため注意が必要です。

■ 競合発生手順
新規に作成したロールでテーブルを作成します。

(node1のデータベースに SQLを投入)=# CREATE ROLE testuser SUPERUSER LOGIN;=# \c testuser=# CREATE TABLE test01 (id integer primary key);=# \d
スキーマ |

リレーションの一覧
名前 |
型

| 所有者
----------+--------+----------+---------public | test01 | テーブル | testuser(1 行)(node2のデータベースに SQLを投入)=# \d
リレーションがありません。
※ ロールなどはレプリケーション対象とならないので、そのロールで作成されたテーブルもレプリケーション対象とならない

データベースログを確認し、ERRO R が継続して出力されていれば競合が発生しています。

Page 268 of 285

PostgreSQL Enterprise Consortium(node2のデータベースログ)
LOG: starting background worker process "bdr (6371561413984311673,2,42879,)->bdr(6371553825031594764,1,"
ERROR: role "testuser" does not exist
CONTEXT: during DDL replay of ddl statement: CREATE TABLE public.test01 (idpg_catalog.int4 , CONSTRAINT test01_pkey PRIMARY KEY (id) ) WITH (oids=OFF)
apply QUEUED_DDL in commit 1/15DD2278, xid 3958054 commited at 2017-01-06
17:37:58.608995+09 (action #2) from node (6371553825031594764,1,42892)
LOG: worker process: bdr (6371561413984311673,2,42879,)->bdr (6371553825031594764,1,(PID 30136) exited with exit code 1※ 権限は関係なく、同名のロールが作成されれば、競合は解決する

レプリケーションを再開させるためにロールなどのグローバルデータを作成します。

(node2のデータベースに SQLを投入)=# CREATE ROLE testuser LOGIN;=# \d
リレーションの一覧
スキーマ | 名前 |型
| 所有者----------+--------+----------+---------public | test01 | テーブル | testuser(1 行)

7.5.4.4.5. ロ ックの競 合 と デッド ロ ックの中 断

1. ロックの競合とデッドロックの中断■ 競合概要一つのノードで取得したロックともう片方のノードで取得したロックが、デッドロック状態となるケースを検証しました。

■ 検証結果異なるノードで実行されたトランザクションでデッドロック状態が発生した場合、ロックが解除されるまでロック待ちが発生します。 Po stgreSQ L においてデッドロックが発生した場合、デッドロックを引き起こすトランザクションが自動でロールバックされますが、 異なるノード間で発生したデッドロックについては検出できないため、ロック待ち状態が続きます。

■ 競合発生手順
下記テーブルを初期状態とします。

(node1のデータベースに SQLを投入)=# CREATE TABLE sample (aid integer primary key, range daterange, price integer);=# ALTER TABLE sample ADD EXCLUDE USING gist (price WITH =, range WITH &&);=# INSERT INTO sample VALUES(1, '[2012-04-16, 2012-04-20]', 10000);=# INSERT INTO sample VALUES(2, '[2012-04-21, 2012-04-30]', 12000);

Page 269 of 285

PostgreSQL Enterprise Consortium(node1のデータベースを確認)=# SELECT * FROM sample;
aid |
range
| price-----+-------------------------+------1 | [2012-04-16,2012-04-21) | 10000
2 | [2012-04-21,2012-05-01) | 12000(2 行)(node2のデータベースを確認)=# SELECT * FROM sample;
aid |
range
| price-----+-------------------------+------1 | [2012-04-16,2012-04-21) | 10000
2 | [2012-04-21,2012-05-01) | 12000(2 行)

1 つのノードからテーブルをロックし、もう片方のノードからはU PDATE を実行し、ロック待ちの影響を受ける事を確認します。 ロックが解除されるまで、B DR
適用プロセスはロック待ちが発生します。

(node1のデータベースに SQLを投入)=# BEGIN;=# LOCK TABLE sample IN ACCESS EXCLUSIVE MODE;=# SELECT pg_sleep(10);=# END;(node2のデータベースに SQLを投入)=# UPDATE sample SET price = 14000 WHERE aid = 2;

(ロック待ちなので競合ログが出力されない)=# \x=# SELECT * FROM bdr.bdr_conflict_history ORDER BY conflict_id DESC LIMIT 1;

7.5.4.4.6. そ の他
自動的には解決出来ないデータの相違が発生した場合は、以下設定を使用して手動で調整する必要があります。
※ レプリケーション環境を破壊することが可能であるため使用する際には注意が必要です。
項番

1

パラ メ ータ説明b dr.do _n o t_rep licate(b o o lean ) このパラメータセットを持つトランザクションで行われた変更は、他ノードへのレプリケーションのためにキューに格納されない。

=# CREATE TABLE test (id int primary key, name text);=# \qPage 270 of 285

PostgreSQL Enterprise Consortium(node1に対してのみレコード追加を実行）$ export PGOPTIONS='-c bdr.do_not_replicate=on'$ psql -h node1 bdrtest=# INSERT INTO test values(1, 'test');=# SELECT * from test;
id | name----+-----1 | test
(1 行)
(node2には反映されていないことを確認)=# SELECT * from test;
id | name----+-----(0 行)
項番

パラ メ ータ説明b dr.skip _ddl_rep licatio n (b o o lean ) DDL による変更のレプリケーションをスキップする。
一部のノードのみに DDL を実行したい場合に有効。

2

スーパーユーザのみが設定可能。

(node1にのみテーブルを作成）=# BEGIN;=# SET LOCAL bdr.skip_ddl_replication = true;=# CREATE TABLE skip (id int primary key, name text);=# END;=# \d
リレーションの一覧
スキーマ | 名前 |型
| 所有者----------+--------+----------+---------public | sample | テーブル | postgres
public | skip | テーブル | postgres
public | test | テーブル | postgres(3 行)(node2には反映されていないことを確認)=# \dリレーションの一覧
スキーマ | 名前 |型
| 所有者----------+--------+----------+---------public | sample | テーブル | postgres
public | test | テーブル | postgres
(2 行)

項番

3

パラ メ ータ説明b dr.p ermit_u n safe_ddl_co mman ds(b o o lean ) 安全にレプリケーションできないスキーマの変更を許可する。
スーパーユーザのみが設定可能。
以下のようなデフォルトでは実行が許可されていないクエリを実行可能。

* CREATE TAB LE AS* ALTER TAB LE ... ADD CO LU MN ... DEFAU LT* CREATE MATERIALIZED VIEW
* REFRESH MATERIALIZED VIEW

Page 271 of 285

PostgreSQL Enterprise Consortium(デフォルトではエラーとなる)=# CREATE TABLE test2 AS SELECT * from test;
ERROR: CREATE TABLE AS is not supported when bdr is active(エラーが出ないことを確認)=# BEGIN;=# SET LOCAL bdr.permit_unsafe_ddl_commands = true;=# CREATE TABLE test2 AS SELECT * from test;=# END;

7.5.4.5. 検証結果
一部の競合パターンにて、意図しない動作が発生するため、現段階では競合が発生しないパターンで利用すべきと考えます。

7.5.5. ノ ー ド障害と復旧
7.5.5.1. 検証の目的
複数ノードで構成されるクラスタ環境内の1 ノードに障害が発生した場合でも、他ノードで継続利用可能か否かを確認します。 また、障害が発生したノードをクラスタ環境に復旧させる手順を確認します。

7.5.5.2. 検証内容
本検証では2 台で構成された B DR クラスタに対して、下記を実施しました。

1. ノード障害
2. ノード復旧

7.5.5.3. 検証環境
こちらの環境 で検証を実施しました。

7.5.5.4. 検証手順
7.5.5.4.1. ノ ード 障 害

1. B DR の各ノード状態確認
B DR の各ノード間のレプリケーションが正常に動作していることを「 b dr.b dr_n o des 」テーブルを用いて確認します。

Page 272 of 285

PostgreSQL Enterprise Consortium(node1にて実施)=# SELECT node_name, node_local_dsn, node_status, node_init_from_dsn FROM bdr.bdr_nodes;-[ RECORD 1 ]------+-----------------------------------node_name| node1
node_local_dsn| host=node1 port=5432 dbname=bdrtest
node_status| r<-- r(正常)であることを確認
node_init_from_dsn |-[ RECORD 2 ]------+-----------------------------------node_name| node2
node_local_dsn| host=node2 port=5432 dbname=bdrtest
node_status| r<-- r(正常)であることを確認
node_init_from_dsn | host=node1 port=5432 dbname=bdrtest<-- node1から作成されていることを確認

2. 検証用テーブルとデータの作成
p gb eb ch を利用して、検証時に利用するテーブルとデータを作成します。

(node1にて実施)$ pgbench -i -s 10 bdrtest

3. トランザクション実行
p gb en ch を利用して、ノード1 にトランザクションを継続的に実行します。 ノード2 に障害が発生した場合に、ノード1 に対して実行したトランザクションにエラーが発生するか否かを確認します。

$ pgbench -h node1 -c 10 -t 100000 bdrtest
starting vacuum...end.

4. ノード障害
ノード2 で動作するPo stgreSQ L を停止させます。

(node2にて実施)$ pg_ctl stop -m i
waiting for server to shut down..... done
server stopped

5. ノード1 のログ確認
ノード2 障害時に出力されるログメッセージを確認します。 下記メッセージは出力されますが、p gb en ch のトランザクションは継続して実行可能です。

$ tail -f [PostgreSQLのログファイル]
LOG: starting background worker process "bdr (6367633348875313343,1,34478,)->bdr(6369931070716042622,2,"
ERROR: establish BDR: could not connect to server: Connection refused
Is the server running on host "node2" (192.168.1.3) and accepting
TCP/IP connections on port 5432?

6. B DR の各ノード状態確認
ノード2 障害後、ノード状態が変化するか否かを「 b dr.b dr_n o des 」テーブルを用いて確認します。

Page 273 of 285

PostgreSQL Enterprise Consortium(node1のデータベースに接続)=# SELECT node_name, node_local_dsn, node_status, node_init_from_dsn FROM bdr.bdr_nodes;-[ RECORD 1 ]------+-----------------------------------node_name| node1
node_local_dsn| host=node1 port=5432 dbname=bdrtest
node_status| r
node_init_from_dsn |-[ RECORD 2 ]------+-----------------------------------node_name| node2
node_local_dsn| host=node2 port=5432 dbname=bdrtest
node_status| r<-- r(正常)の状態のまま
node_init_from_dsn | host=node1 port=5432 dbname=bdrtest※ 状態変化は確認できませんでした。

7. ノードの切り離し障害が発生したノード2 を切り離しするため、「 b dr.b dr_p art_b y_n o de_n ames 」関数を実行します。

(node1のデータベースに接続)=# SELECT bdr.bdr_part_by_node_names(ARRAY['node2']);bdr_part_by_node_names-----------------------(1 row)

8. ノードの切り離し結果確認
ノードの切り離し結果を「 b dr.b dr_n o des 」テーブルを用いて確認します。

(node1のデータベースに接続)=# SELECT node_name, node_local_dsn , node_status, node_init_from_dsn FROM
bdr.bdr_nodes;-[ RECORD 1 ]------+-----------------------------------node_name| node1
node_local_dsn| host=node1 port=5432 dbname=bdrtest
node_status| r
node_init_from_dsn |-[ RECORD 2 ]------+-----------------------------------node_name| node2
node_local_dsn| host=node2 port=5432 dbname=bdrtest
node_status| k<-- k(削除)であることを確認
node_init_from_dsn | host=node1 port=5432 dbname=bdrtest

9. システムカタログの残データ削除
本手順は推奨される手順ではありませんが、削除状態のノードが「 b dr.b dr_n o des 」テーブルに残っているとノード復旧が実行できないため暫定的な対処です。 B DR のIssu es で「 b dr.b dr_co n n ectio n s 」のデータも削除する事が提案されていたので、こちらも暫定的な手順ですが実行します。 詳細は下記をご参照下さい。

h ttp s://gith u b .co m/2n dQ u adran t/b dr/issu es/97
h ttp s://gith u b .co m/2n dQ u adran t/b dr/issu es/126(node1のデータベースに接続)=# DELETE FROM bdr.bdr_connections USING bdr.bdr_nodes WHERE node_status = 'k' AND"(node_sysid, node_timeline, node_dboid) = (conn_sysid, conn_timeline, conn_dboid);=# DELETE FROM bdr.bdr_nodes where node_status = 'k';

10. トランザクション状態実行状態確認

Page 274 of 285

PostgreSQL Enterprise Consortium
手順1 で実行した p gb en ch にエラーが発生してないことを確認します。本検証ではエラーは発生しませんでした。

7.5.5.4.2. ノ ード 復 旧B DR ノードの復旧方法(ノード追加)する場合、既存ノードのデータベースと復旧させるノードのデータベースのスキーマおよびデータを同期させる必要があります。 ノード間のデータコピーには、論理コピーと物理コピーの2 つの手法があります。
h ttp ://b dr-p ro ject.o rg/do cs/stab le/fu n ctio n s-n o de-mgmt.h tml#FU NCTIO N-B DR-G RO U P-JO IN
表 7.8 コピー取得方法
項番1

コ ピー取得b dr.b dr_gro u p _jo in 関

説明

備考

ユーザが指定したノード内データベースのスキーマとデータダンプを取得

p g_du mp コマンドに相当

ユーザが指定したノード上の全てのデータベースのコピーを取得

p g_b aseb acku p コマンド数実行

2

b dr_in it_co p yコマンドに相当■ 論理コピーによる復旧
1. トランザクション実行
p gb en ch を利用して、ノード1 にトランザクションを継続的に実行します。 ノード2 に復旧する際に、ノード1 に対して実行したトランザクションの停止が必要か否かを確認します。

$ pgbench -h node1 -c 10 -t 100000 bdrtest
starting vacuum...end.

2. ノード2 のPo stgreSQ L 起動
ノード2 上で動作するPo stgreSQ L を起動させます。

$ pg_ctl start

3. B DR の無効化
ノード2 のデータベースから B DR を削除し、B DR 拡張機能を削除します。

(node2のデータベースに接続)=# SELECT bdr.remove_bdr_from_local_node(true);WARNING: forcing deletion of possibly active BDR node
NOTICE: removing BDR from node
NOTICE: BDR removed from this node. You can now DROP EXTENSION bdr and,if this is the last BDR node on this PostgreSQL instance, remove bdr fromshared_preload_libraries.
remove_bdr_from_local_node---------------------------=# DROP EXTENSION bdr;
DROP EXTENSION

4. データベース削除
B DR で利用したデータベースを削除します。

(node2のデータベースに接続)=# DROP DATABASE bdrtest ;DROP DATABASE※ 接続が残っており、削除できない場合はPostgreSQLを再起動

5. データベースの再作成

Page 275 of 285

PostgreSQL Enterprise Consortium

B DR で利用するデータべースを再度作成します。

(node2のデータベースに接続)=# CREATE DATABASE bdrtest;

6. B DR 有効化
無効化した B DR を再度有効化します。

(node2のデータベースに接続)=# CREATE EXTENSION btree_gist;
CREATE EXTENSION=# CREATE EXTENSION bdr;
CREATE EXTENSION

7. ノードの追加
ノードを追加(復旧)させるため、「 b dr.b dr_gro u p _jo in 」関数を実行します。

(node2のデータベースに接続)=# SELECT bdr.bdr_group_join(
local_node_name := 'node2',
node_external_dsn := 'host=node2 port=5432 dbname=bdrtest',
join_using_dsn := 'host=node1 port=5432 dbname=bdrtest');
bdr_group_join---------------(1 行)

8. ノード追加の確認待ち
ノードが追加されたことを確認するため、「 b dr.b dr_n o de_jo in _w ait_fo r_ready」関数を実行します。

(node2のデータベースに接続)=# SELECT bdr.bdr_node_join_wait_for_ready();※ トランザクションが実行中の場合、上記関数の結果が戻りません。

n o de1 のPo stgreSQ L ログファイルに下記メッセージが出力された後、復旧の処理が開始されません。

(node1のログメッセージ抜粋)
LOG: logical decoding found initial starting point at 0/BB399BF0
DETAIL: 10 transactions need to finish.

p gb en ch コマンドで実行中のトランザクションを停止すると、下記ログメッセージが出力され、復旧処理が開始されます。

(node1のログメッセージ抜粋)
LOG: logical decoding found consistent point at 0/B2895648
DETAIL: There are no running transactions.
LOG: exported logical decoding snapshot: "00046FE0-1" with 0 transaction IDs

9. B DR の各ノード状態確認
ノード2 が追加されたことを「 b dr.b dr_n o des 」テーブルの情報から確認します。

Page 276 of 285

PostgreSQL Enterprise Consortium(node1のデータベースに接続)=# SELECT node_name, node_local_dsn, node_status, node_init_from_dsn FROM bdr.bdr_nodes;-[ RECORD 1 ]------+-----------------------------------node_name| node1
node_local_dsn| host=node1 port=5432 dbname=bdrtest
node_status| r<-- r(正常)であることを確認
node_init_from_dsn |-[ RECORD 2 ]------+-----------------------------------node_name| node2
node_local_dsn| host=node2 port=5432 dbname=bdrtest
node_status| r<-- r(正常)であることを確認
node_init_from_dsn | host=node1 port=5432 dbname=bdrtest<-- node1から作成されていることを確認■ 物理コピーによる復旧
1. トランザクション実行
p gb en ch を利用して、ノード1 にトランザクションを継続的に実行します。 ノード2 に復旧する際に、ノード1 に対して実行したトランザクションの停止が必要か否かを確認します。

$ pgbench -h node1 -c 10 -t 100000 bdrtest
starting vacuum...end.

2. ノード2 のPo stgreSQ L 停止確認
ノード2 上で動作するPo stgreSQ L が停止していることを確認します。

(node2にて実施)$ pg_ctl status
pg_ctl: no server running

3. 物理コピーの取得
ノード2 上で「 b dr_in it_co p y」コマンドを実行し、n o de1 上のコピーを取得します。

(node2にて実施)$ rm -rf $PGDATA/*$ bdr_init_copy -D $PGDATA -n node2 -h node1 -p 5432 -d bdrtest--local-host=node2 --local-port=5432 --local-dbname=bdrtest
bdr_init_copy: starting ...
Getting remote server identification ...
Detected 1 BDR database(s) on remote server
Updating BDR configuration on the remote node:bdrtest: creating replication slot ...
bdrtest: creating node entry for local node ...
Creating base backup of the remote node...
50357/50357 kB (100%), 1/1 tablespace
Creating restore point on remote node ...
Bringing local node to the restore point ...
トランザクションログをリセットします。

Initializing BDR on the local node:bdrtest: adding the database to BDR cluster ...
All donen o de1 のPo stgreSQ L ログファイルに下記メッセージが出力された後、復旧の処理が開始されません。

Page 277 of 285

PostgreSQL Enterprise Consortium(node1のログメッセージ抜粋）
LOG: logical decoding found initial starting point at 0/BB399BF0
DETAIL: 10 transactions need to finish.

p gb en ch コマンドで実行中のトランザクションを停止すると、下記ログメッセージが出力され、 復旧処理が開始されます。

(node1のログメッセージ抜粋
LOG: logical decoding found consistent point at 0/B2895648
DETAIL: There are no running transactions.
STATEMENT: SELECT
pg_create_logical_replication_slot('bdr_25434_6369931070716042622_2_25434__', 'bdr');

4. B DR の各ノード状態確認
ノード2 が追加されたことを「 b dr.b dr_n o des 」テーブルの情報から確認します。

(node1のデータベースに接続)
bdrtest=# SELECT node_name, node_local_dsn, node_status, node_init_from_dsn FROM
bdr.bdr_nodes;-[ RECORD 1 ]------+-----------------------------------node_name| node1
node_local_dsn| host=node1 port=5432 dbname=bdrtest
node_status| r<-- r(正常)であることを確認
node_init_from_dsn |-[ RECORD 2 ]------+-----------------------------------node_name| node2
node_local_dsn| host=node2 port=5432 dbname=bdrtest
node_status| r<-- r(正常)であることを確認
node_init_from_dsn | host=node1 port=5432 dbname=bdrtest<-- node1から作成されていることを確認

7.5.5.5. 検証結果
ノード障害が発生した際にも、他のノードではトランザクションを継続実行することが可能でした。 障害ノードを復旧させるためにはデータ操作が禁止されているシステムカタ
ログのデータ削除が必要でした。

7.5.5.6. 備考
ノード障害後および復旧中に DDL を実行すると下記のエラーが出力されます。

=# CREATE TABLE test2(id int);
ERROR: No peer nodes or peer node count unknown, cannot acquire global lock
HINT: BDR is probably still starting up, wait a while

7.6. BDR性能検証
7.6.1. 更新性能検証結果
7.6.1.1. 検証目的
本試験はB DR の本来のユースケースである、 高レイテンシ環境との双方向レプリケーション環境における更新クエリのレスポンス改善とそれに伴う性能改善を確認したものです。

7.6.1.1.1. 検 証 内 容Page 278 of 285

PostgreSQL Enterprise Consortium
今回は 2014 年度検証報告書 （可用性編） に記載された東京-シンガポール間の回線情報（応答速度76.95ms 、帯域幅0.16G b its/s ）をもとに、 以下のような環境を構築しp gb en ch を実行しました。

通常のストリーミングレプリケーションであれば、 更新クエリを実行したい場合はシンガポールのマスタノードに更新要求を実施する必要がありますが、 B DR では最寄りのノードに対して更新要求を実施することが可能です。

7.6.1.1.2. 検 証 環 境
こちらの環境 で検証を実施しました。

7.6.1.1.3. 検 証 手 順

1. 環境構築p gb en ch を実行する準備を行います。（ B DR, SR 環境両方）# pgbench -i test -U postgres -s 10
NOTICE: table "pgbench_history" does not exist, skipping
NOTICE: table "pgbench_tellers" does not exist, skipping
NOTICE: table "pgbench_accounts" does not exist, skipping
NOTICE: table "pgbench_branches" does not exist, skipping
creating tables...
100000 of 100000 tuples (100%) done (elapsed 0.25 s, remaining 0.00 s).
vacuum...
set primary keys...
done.

n o de1, n o de2 ともにテーブルが作成されていることを確認します。

# psql -U postgres test -c "\d"スキーマ |

リレーションの一覧
名前
|

型
| 所有者
----------+------------------+----------+---------public | pgbench_accounts | テーブル | postgres
public | pgbench_branches | テーブル | postgres
public | pgbench_history | テーブル | postgres
public | pgbench_tellers | テーブル | postgres(4 行)# /usr/pgsql-9.4/bin/psql -U postgres -h node2 test -c "\d"スキーマ |

リレーションの一覧
名前
|

型
| 所有者
----------+------------------+----------+---------public | pgbench_accounts | テーブル | postgres
public | pgbench_branches | テーブル | postgres
public | pgbench_history | テーブル | postgres
public | pgbench_tellers | テーブル | postgres(4 行)

帯域制限及び遅延設定を行います。

Page 279 of 285

PostgreSQL Enterprise Consortium（ clientはnode2向け通信のみ制限)

tc qdisc add dev eth0 root handle 1:0 htb
tc class add dev eth0 parent 1:0 classid 1:10 htb rate 160Mbit
tc filter add dev eth0 protocol ip parent 1:0 prio 1 u32 match ip dst 192.168.0.12/32
flowid 1:10
tc qdisc add dev eth0 parent 1:10 handle 10:1 netem delay 38ms 1ms（ node1はnode2向け通信のみ制限）

tc qdisc add dev enp0s25 root handle 1:0 htb
tc class add dev enp0s25 parent 1:0 classid 1:10 htb rate 160Mbit
tc filter add dev enp0s25 protocol ip parent 1:0 prio 1 u32 match ip dst 192.168.0.12/32
flowid 1:10
tc qdisc add dev enp0s25 parent 1:10 handle 10:1 netem delay 38ms 1ms（ node2は両ノード向け通信とも制限）

tc qdisc add dev enp0s25 root handle 1:0 htb
tc class add dev enp0s25 parent 1:0 classid 1:0 htb rate 160Mbit
tc qdisc add dev enp0s25 parent 1:0 handle 10:1 netem delay 38ms 1ms

帯域制限の結果以下のような環境になります。
表 7.9 帯域制限後のネットワーク情報

Client/Server

client

node1

node2

clien t－

遅延： 0.5 ms
帯域幅： 934 Mb its/s

遅延： 76ms
帯域幅： 160 Mb its/sn o de1

遅延： 0.5 ms
帯域幅： 934 Mb its/s－

遅延： 76ms
帯域幅： 160 Mb its/sn o de2

遅延： 76ms
帯域幅： 160 Mb its/s

遅延： 76ms
帯域幅： 160 Mb its/s－

2. レスポンスタイム確認
更新クエリの応答速度を確認します。

# time psql -h node1 -x -c "UPDATE pgbench_branches SET bbalance = bbalance + 100 WHERE
bid = 1" test
UPDATE 1
real
user
sys

0m0.025s
0m0.001s
0m0.001s# time psql -h node2 -x -c "UPDATE pgbench_branches SET bbalance = bbalance + 100 WHERE
bid = 1" test
UPDATE 1
real
user
sys

0m0.259s
0m0.001s
0m0.000s（ネットワーク遅延に伴いレスポンスが低下していることを確認）

3. SR 環境の性能試験
非同期SR 構成のマスターに対して p gb en ch を実施します。

Page 280 of 285

PostgreSQL Enterprise Consortium# pgbench -U postgres -p 5433 -h node2 -s 10 -c 10 test -T 180
scale option ignored, using count from pgbench_branches table (10)starting vacuum...end.
transaction type: TPC-B (sort of)scaling factor: 10
query mode: simple
number of clients: 10
number of threads: 1
duration: 180 s
number of transactions actually processed: 3078
latency average: 584.795 ms
tps = 17.042442 (including connections establishing)tps = 17.057455 (excluding connections establishing)

4. B DR 環境の性能試験
競合が発生しているか確認するために、「 b dr.b dr_co n flict_h isto ry」を参照し競合履歴数を確認します。

# psql -h node1 -x -c "SELECT count(*) FROM bdr.bdr_conflict_history;" test-[ RECORD 1 ]count | 52265# psql -h node2 -x -c "SELECT count(*) FROM bdr.bdr_conflict_history;" test-[ RECORD 1 ]count | 132935

B DR 環境のノードに p gb en ch を実施します。

# pgbench -U postgres -h node1 -s 10 -c 10 test -T 180
scale option ignored, using count from pgbench_branches table (10)starting vacuum...end.
transaction type: TPC-B (sort of)scaling factor: 10
query mode: simple
number of clients: 10
number of threads: 1
duration: 180 s
number of transactions actually processed: 16677
latency average: 107.933 ms
tps = 92.598796 (including connections establishing)tps = 92.602290 (excluding connections establishing)

競合が発生していないことを確認します。

# psql -h node1 -x -c "SELECT count(*) FROM bdr.bdr_conflict_history;" test-[ RECORD 1 ]count | 52265# psql -h node2 -x -c "SELECT count(*) FROM bdr.bdr_conflict_history;" test-[ RECORD 1 ]count | 132935

7.6.1.1.4. 検 証 結 果
本検証では以下を確認することが出来ました。
レスポンスタイムの低減

Page 281 of 285

PostgreSQL Enterprise Consortium
低レイテンシのサーバに更新クエリを実行可能であるため、レスポンスタイムを低減することが出来ました。

TPS の向上
レスポンスタイムが低減されたため、TPS の向上が見られました。
"17.057455" -> "92.602290"（約5.4 倍）
ただし、本検証では片系のみに変更を実施し競合が発生しないようにした試験だったため、このような結果になったものと思われます。
本検証で使用しているp gb en ch （ TCP-B ライク）のような、現在の値に対して加算していくような処理の場合、結果整合で競合を解決することができないため、 絶対に競合が発生しない構成が必要です。
例）更新するテーブルを拠点ごとに分ける等

7.7. まとめ7.7.1. BDR検証まとめ
本検証では、B DR の機能や特徴および主なユースケースを机上調査を実施した上で、B DR の動作検証および性能検証を実施しました。 本検証で実施した B DR の動
作検証の結果は下表の通りです。
表 7.10 結果評価基準
記号

意味

○

問題なし。

△

利用時に問題になるケースがある。

×

対応していない。事実上使えない。
表 7.11 B DR 検証結果
項番

検証概要

結果

補足

1

ノード追加/削除△

ノード削除はオンラインで実行可能。ノード追加時にトランザクションの停止が必要。 また、削除したノードを追加する場合にはシステムカタログの操作が必要。

2

グローバルシーケンス○

シーケンスの競合を防ぐことが可能。ただし、マニュアルに記載された制限事項については確認が必要。

3

選択的レプリケーション○

B DR を利用して任意のテーブルのデータ集約等を実現可能

4

更新処理競合時の動作△更新が競合するパターンで意図しない動作が発生し、競合解消のため手動での操作が必要。

5

ノード障害と復旧△

ノード復旧時にはトランザクションの停止が必要。 また、障害が発生したノードを復旧させる際にシステムカ
タログの操作が必要。

本検証で利用したバージョン(1.0.2)では更新が競合するパターンで意図しない動作が発生するため、B DR を適用する場合、競合が発生しないようなアプリケーション設計
やテーブル設計が必要になります。
例) 更新するテーブルを拠点ごとに分ける等
ノード障害時の運用においても、一般的でないシステムカタログの操作を必要とするといった今後の改善が必要と思われる結果が確認されました。
また、B DR の選択的レプリケーションを用いることで、Po stgreSQ L のストリーミング・レプリケーションでは実現できない、テーブル単位のレプリケーションが実現可能なことを確認できました。 上記機能を用いて、システム間のデータ連携(データ集約等)を柔軟に実現できると考えております。
性能検証の結果より、ユースケースで想定している「遠距離拠点間で双方向に更新する」場合に、レスポンスタイムの低減と処理向上につながることが確認できました。
机上で調査した通り、B DR を適用することで「遠距離拠点間のトランザクション性能改善」や選択的レプリケーション機能を利用した「柔軟なデータ連携」を実現できると考えます。

7.8. 参考文献[B DR] Po stgres-B DR ドキュメント h ttp ://b dr-p ro ject.o rg/do cs/stab le/7.8.1. 関数一覧

Page 282 of 285

PostgreSQL Enterprise Consortium
表 7.12 関数一覧
項番

関数

参照ド キュメ ント

1b dr.b dr_gro u p _create

fu n ctio n s-n o de-mgmt.h tml

2

b dr.b dr_gro u p _jo in

fu n ctio n s-n o de-mgmt.h tml

3

b dr.b dr_p art_b y_n o de_n ames

fu n ctio n s-n o de-mgmt.h tml

4

b dr.remo ve_b dr_fro m_lo cal_n o defu n ctio n s-n o de-mgmt.h tml

5b dr.b dr_n o de_jo in _w ait_fo r_readyfu n ctio n s-n o de-mgmt.h tml

6

b dr.tab le_set_rep licatio n _sets

fu n ctio n s-rep licatio n -sets.h tml

7

b dr.tab le_get_rep licatio n _sets

fu n ctio n s-rep licatio n -sets.h tml

8

b dr.co n n ectio n _get_rep licatio n _sets fu n ctio n s-rep licatio n -sets.h tml

7.8.2. シ ステム カタログ一覧
表 7.13 システムカタログ一覧
項番

システムカ タ ロ グ

参照ド キュメ ント

1b dr.b dr_co n flict_h isto ry

catalo g-b dr-co n flict-h isto ry.h tml

2

b dr.b dr_n o descatalo g-b dr-n o des.h tml

3

b dr.b dr_co n n ectio n scatalo g-b dr-co n n ectio n s.h tml

Page 283 of 285

PostgreSQL Enterprise Consortium

8. まとめ本文書ではPo stgreSQ L におけるレプリケーション技術として標準機能として組み込まれているストリーミングレプリケーションと ロジカルレプリケーションについて取り上げました。さらに、マルチマスタ構成に対応した B i-Directio n al Rep licatio n (B DR)についても取り上げました。
ストリーミングレプリケーションは、2010 年9 月にリリースされた Po stgreSQ L9.0 で標準機能として組み込まれ、徐々に実運用での利用される機会が増えるとともに、実運用で必要となる機能改善がその後のバージョンアップで継続的に行われています。
本文書ではストリーミングレプリケーション構成を構築する基本的な手順を確認した上で、レプリケーションスロット、遅延レプリケーション、W AL 圧縮といった新しい機能に対しても、設定から実際の動作を確認するまでの具体的な手順を整理することができました。障害発生時の運用についてはPo stgreSQ L9.5 から提供されるようになった
p g_rew in d コマンドを使うことで、これまで必要とされていたフルバックアップ取得を不要とする運用が可能になっていることを確認できました。さらに、複数台のスレーブが存在するあるいはカスケードレプリケーションといったより複雑な構成で障害が発生した場合の運用手順についても確認しました。
ロジカルレプリケーションは、2017 年10 月にリリースされた Po stgreSQ L10.0 で標準機能として組み込まれました。ストリーミングレプリケーションと類似したアーキテクチャを持ちながら、テーブル単位や更新種別単位で柔軟なレプリケーション形態を取ったり、複製先を更新することができる等の特徴を有しています。
本文書ではロジカルレプリケーション構成を構築する基本的な手順、同期レプリケーション、複数Su b scrip tio n へのレプリケーション、カスケード構成といった応用的な使い方、監視方法や障害発生時の挙動、レプリケーション開始後のテーブル追加、定義変更の手順について検証しました。検証結果より、現状のロジカルレプリケーションは柔軟にテーブル単位、更新種別単位で複製できることによりレプリケーションの活用範囲が広がった一方で、更新競合を解消する機能が貧弱なことやレプリケーションされないSQ L 、オブジェクトが多数あるといった制約事項もあるため、現状では更新競合が発生しないよう複製先を参照用途に限定して利用するのが現実的と考えます。

B DR は、現状のストリーミングレプリケーションでは不可能なマルチマスタ構成に対応することから、シングルマスタ構成に起因する課題解消が期待されます。今回の検証では、B DR の特徴、ユースケース、メカニズムを類似機能、製品と机上比較した上で、更新処理競合時の動作、ノード障害時の動作、更新性能についてそれぞれ検証しました。更新性能の検証結果より、ユースケースで想定している「遠距離拠点間で双方向に更新する」場合に、レスポンスタイムの低減とTPS 向上につながることがわかりました。
一方で、現時点のバージョンでは更新が競合するパターンで意図しない動作が発生するため、現段階では競合が発生しないパターンで利用すべきと考えます。そこで、
B DR の利用時は競合が発生しにくいテーブル設計にすることが望ましいです。また、ノード障害時の運用においても、一般的でないシステムカタログの操作を必要とするといった今後の改善が必要と思われる結果が確認されました。
これらの結果から、B DR についてはユースケースと実際の利用用途が合致しているかを見極めた上で利用するかどうかを判断する必要があるといえます。
今回の検証結果がストリーミングレプリケーション構成を実際に利用している方々の運用改善につながること、またロジカルレプリケーションやB DR を利用すべきかどうかを判断する際の参考情報として活用いただけることを期待しています。

Page 284 of 285

PostgreSQL Enterprise Consortium

9. 著者（企業・団体名順）
版
第1.0 版(2016 年度W G 3)

第2.0 版(2017 年度W G 3)

所属企業・ 団体名

部署名

氏名

株式会社アシスト

データベース技術本部

竹内 尚也

株式会社アシスト

データベース技術本部

柘植 丈彦

株式会社オージス総研

プラットフォームサービス本部 IT基盤技術部

大西 斉

TIS 株式会社

IT基盤技術本部 IT基盤技術推進部

中西 剛紀

日本電信電話株式会社

オープンソースソフトウェアセンタ

坂田 哲夫

株式会社富士通ソーシアルサイエンスラボラトリ

プラットフォームインテグレーション本部 第四システム部

小山田 政紀株式会社富士通ソーシアルサイエンスラボラトリ

プラットフォームインテグレーション本部 第四システム部

高橋 勝平

株式会社富士通ソーシアルサイエンスラボラトリ

プラットフォームインテグレーション本部 第四システム部

香田 紗希

株式会社アシスト

サービス事業部 サポートセンター

神谷 美恵子株式会社アシスト

データベース技術本部 技術統括部

竹内 尚也

株式会社アシスト

データベース技術本部 技術統括部

柘植 丈彦

株式会社オージス総研

プラットフォームサービス本部 IT基盤技術部

大西 斉

TIS 株式会社

IT基盤技術本部

中西 剛紀

株式会社富士通ソーシアルサイエンスラボラトリ

ソリューション開発センター ソリューションビジネス部

小山田 政紀株式会社富士通ソーシアルサイエンスラボラトリ

第二システム事業本部 第三システム部

香田 紗希

Page 285 of 285