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

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

PostgreSQL Enterprise Consortium

目次
目次
1. ライセンス2. はじめに2.1. PostgreSQLエンタープライズコンソーシアムとWG3について
2.2. 本資料の概要と目的
2.3. 本資料の構成
2.4. 想定読者
2.5. 参考文献
3. PostgreSQLにおけるレプリケーション
3.1. レプリケーションの目的
3.1.1. 可用性の向上
3.1.2. 性能向上
3.1.2.1. 参照の同期
3.1.2.2. 更新による一貫性の維持
3.2. 代表的なレプリケーションの手法
3.2.1. シングルマスタ
3.2.1.1. ストレージレプリケーション
3.2.1.2. トリガベースレプリケーション
3.2.1.3. クエリベースレプリケーション
3.2.1.4. ストリーミングレプリケーション
3.2.2. マルチマスタ
3.2.2.1. Bi-Directional Replication
3.2.2.2. Bucardo
3.3. 参考文献
4. ストリーミングレプリケーション
4.1. はじめに
4.1.1. PostgreSQLのストリーミングレプリケーションの特徴
4.1.2. 想定の構成
4.2. SR環境構築時の設定項目、推奨値
4.2.1. SRにおける目的別の設定
4.2.2. SR環境の設定手順
4.2.3. アーカイブモード運用
4.2.4. レプリケーションスロット
4.2.5. 遅延レプリケーション
4.2.6. WAL圧縮
4.3. SR環境の監視
4.3.1. レプリケーション操作ログの監視
4.3.1.1. 調査の目的
4.3.1.2. 調査結果
4.3.1.3. 調査結果
4.3.2. 同期状況の監視
4.3.2.1. 同期遅延監視
4.3.3. 更新、読み取りの監視
4.3.3.1. 更新・読取監視
4.3.4. スプリットブレインの監視
4.3.4.1. スプリットブレインの定義と調査の目的
4.3.4.2. 監視方法
4.3.4.3. サーバログの監視
4.3.4.4. pg_controldataコマンドによる監視
4.3.4.5. pg_control_recoveryコマンドによる監視
4.3.4.6. pg_is_in_recovery関数による監視
4.4. SR環境の障害時運用
4.4.1. 本文書における用語の定義
4.4.1.1. フェイルオーバ
4.4.1.2. フェイルバック
4.4.1.3. スイッチオーバ
4.4.1.4. スイッチバック
Page 2 of 96

2
5
6
6
6
6
6
7
8
8
8
8
8
8
8
8
9
9
9
9
10
10
10
10
11
11
11
13
13
13
13
17
18
20
22
22
22
22
22
23
23
23
24
24
25
25
25
26
26
27
27
27
27
27
27
28
28

PostgreSQL Enterprise Consortium

4.4.2. 障害時運用手順
4.4.2.1. フェイルオーバ
4.4.2.2. フェイルバック
4.4.2.3. スイッチオーバ
4.4.2.4. スイッチバック
4.4.2.5. スレーブ障害による対処
4.5. まとめ
4.5.1. ストリーミングレプリケーション機能拡張の歴史
4.5.2. PGEConsの今後の取り組み
5. Bi-Directional Replication (BDR)
5.1. はじめに
5.1.1. BDRの特徴
5.1.1.1. BDRとは
5.1.1.2. ユースケース
5.1.1.3. メカニズム
5.1.1.4. 整合性
5.1.1.5. シーケンス
5.1.1.6. 比較表
5.1.1.7. サポート
5.2. 調査、検証の目的
5.3. 調査、検証の前提
5.4. BDR環境構築時の設定と設定手順
5.4.1. BDR環境の設定手順
5.4.1.1. 概要
5.4.1.2. 対象試験サーバ
5.4.1.3. 事前確認
5.5. BDR動作検証
5.5.1. ノードの追加・切り離し
5.5.1.1. 検証の目的
5.5.1.2. 検証内容
5.5.1.3. 検証環境
5.5.1.4. 検証手順
5.5.1.4.1. ノード切り離し
5.5.1.4.2. ノード追加
5.5.1.5. 検証結果
5.5.2. グローバルシーケンス設定
5.5.2.1. 検証の目的
5.5.2.2. 検証環境
5.5.2.3. 検証手順
5.5.2.4. 検証結果
5.5.3. 選択的レプリケーション
5.5.3.1. 検証目的
5.5.3.2. 検証内容
5.5.3.3. 検証環境
5.5.3.4. 検証手順
5.5.3.5. 検証結果
5.5.4. 更新処理競合時の動作
5.5.4.1. 検証目的
5.5.4.2. 検証内容
5.5.4.3. 検証環境
5.5.4.4. 検証手順
5.5.4.4.1. PRIMARY KEYまたはUNIQUE制約
5.5.4.4.2. 外部キー制約
5.5.4.4.3. 排他制約
5.5.4.4.4. グローバルなデータ
5.5.4.4.5. ロックの競合とデッドロックの中断
5.5.4.4.6. その他
5.5.4.5. 検証結果
5.5.5. ノード障害と復旧

29
30
31
35
36
39
40
40
42
43
43
43
43
43
43
44
44
45
45
45
45
46
46
46
46
46
52
52
52
52
52
52
52
54
58
58
58
58
58
60
61
61
61
61
61
65
65
65
65
66
66
67
75
76
79
80
81
83
83
Page 3 of 96

PostgreSQL Enterprise Consortium

5.5.5.1. 検証の目的
5.5.5.2. 検証内容
5.5.5.3. 検証環境
5.5.5.4. 検証手順
5.5.5.4.1. ノード障害
5.5.5.4.2. ノード復旧
5.5.5.5. 検証結果
5.5.5.6. 備考
5.6. BDR性能検証
5.6.1. 更新性能検証結果
5.6.1.1. 検証目的
5.6.1.1.1. 検証内容
5.6.1.1.2. 検証環境
5.6.1.1.3. 検証手順
5.6.1.1.4. 検証結果
5.7. まとめ5.7.1. BDR検証まとめ
5.8. 参考文献
5.8.1. 関数一覧
5.8.2. システムカタログ一覧

83
83
83
83
83
86
89
89
89
89
89
89
90
90
92
93
93
93
93
94
95
96

6. まとめ
7. 著者

Page 4 of 96

PostgreSQL Enterprise Consortium

1. ライセンス
本作品は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-C, TPC-E, tp mC, TPC-H, Q p h H は米国Tran sactio n Pro cessin g Perfo rman ce Co u n cil の商標です
その他、本資料に記載されている社名及び商品名はそれぞれ各社が 商標または登録商標として使用している場合があります 。

Page 5 of 96

PostgreSQL Enterprise Consortium

2. はじめに2.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 が広く活用される事を推進していくこととしました。

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

W G 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 のシステム構成を挙げて、データベースのデータ保全性、サービス継続性の観点か
ら各構成の得失を示しました。
この間、Po stgreSQ L や周辺のO SS ツールの開発が進み、ストリーミングレプリケーションの機能改善や Po stgreSQ L をベースとして双方向レプリケーションがリリースされています。
これらの開発動向と過去の検討結果とを踏まえて、2016 年度は機能拡充が進むストリーミングレプリケーションの運用ノウハウの獲得と、2n d Q u adran t 社が開発・リリースしている Po stgreSQ L ベースの双方向レプリケーションである Po stgres-B RD [Po stgresB DR] の基本機能の調査を実施しました。

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

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

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

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

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

PostgreSQL Enterprise Consortium

2.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 7 of 96

PostgreSQL Enterprise Consortium

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

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

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

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

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

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

3.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] 。ここで紹介する紹介するレプリケーションソフトウェアについても紹介されています。

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

Page 8 of 96

PostgreSQL Enterprise Consortium

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

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

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

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

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

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

Page 9 of 96

PostgreSQL Enterprise Consortium
後日記入予定(27.Feb .2017)

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

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

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

3.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/[p gp o o l2] 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 10 of 96

PostgreSQL Enterprise Consortium

4. ストリーミングレプリケーション
4.1. はじめに
4.1.1. PostgreSQL の ストリ ー ミン グレプリ ケー シ ョ ン の 特徴
Po stgreSQ L のストリーミングレプリケーションは以下を目的とした構成です。
高可用性
バックアップ
参照負荷分散以下の特徴があります。
変更履歴が格納された W AL を操作単位でマスタ側からスレーブ側へ転送することでデータを同期

ストリーミングレプリケーション基本構成図（スレーブ１台）

Po stgreSQ . 9.6 における主な機能を示します。主に 9.3 以降の新機能を対象としています。
完全同期方式 により、スレーブ活用度の向上が期待できる
従来の同期式は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 の設定による保証時点を示す。

以下の図はsyn ch ro n o u s_co mmit の各設定によるパフォーマンス比較(lo cal の値を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% となるように調整

Page 11 of 96

PostgreSQL Enterprise Consortium

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)
通常のアプリケーションではこれほど低下しないと考えられるが、一定の甘受は必要。

レプリケ ーショ ンスロ ット により、スレーブに必要なW AL をマスタが確保し続ける
スレーブの障害時（ネットワーク不調含む）等にマスタでW AL を保持し切れず、W AL ファイルの再利用によりロストする懸念がある。
従来はw al_keep _segmen ts パラメータによる調整、またはアーカイブモード運用が必要であった - w al_keep _segmen ts はW AL 数を指定するものであるため、見積もりは困難。 - アーカイブモードも複数スレーブ構成における不要W AL ファイルの判断はやはり困難。
レプリケーションスロットはW AL の不要W AL ファイルの判断をシステムに任せる事ができる。
レプリケーションのW AL ファイル管理のためにアーカイブモード運用は必要はなく、過去の時点に復旧する(PITR)要件がある場合に設定する。
複数同期スレーブ構成 が可能
従来も複数スレーブ構成は可能であったが、同期スレーブはその内の1 台までという制限があったがそれが取り払われた。
遅延レプリケ ーショ ン機能 により、オペレーションミスを反映させない事が可能にスレーブでの適用を一定時間待機する機能である。マスタでのオペミスが即座にスレーブに伝搬されるのを防ぐのが主な目的。
W AL ファイルの転送は遅延なく処理されるため、データ保全(RPO )の観点では問題ない。同期モードで設定する事もできる。
reco very.co n f のreco very_min _ap p ly_delayパラメータに遅延時間を設定。
従来からの手法として、PITR を使用してオペミス直前時点を指定してリカバリすることで障害回復を図る方法もある。
巻き戻し 機能 (p g_rew in d)により、効率の良いレプリケーション再構成が可能に
旧マスタを分岐時点へ巻き戻す事で、新マスタに追い付く事が可能な状態とする機能である。
巻き戻し後、旧マスタに新マスタのW AL を適用（追い付き）する事で旧マスタを新スレーブとして構成。
従来はp g_b aseb acku p 等による再作成が必要であった。
大規模データベースでは、大幅な時間短縮が期待できる。
下図に巻き戻し機能のイメージを示す。

ストリーミングレプリケーションの運用上の注意点

Page 12 of 96

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

4.1.2. 想定の 構成
今回はストリーミングレプリケーションの基本機能の検証を目的としています。以下の構成を想定しています。
対象バージョンは Po stgreSQ L 9.6
2017/03 時点の最新版
2 ノード構成に限定
基本機能の検証が目的であるため、2 ノードを想定
複数同期スレーブ構成などは来季以降の課題とする
Po stgreSQ L の機能に限定
p gp o o l-II 等のクラスタソフトに関するテーマは対象外とする

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

内容

アーカイブログW AL ファイルが循環して再利用される前に、アーカイブログとして保存する。 これにより全ての変更履歴が保持される。

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

スレーブ側で適用されていないW AL ファイルを保持する。

遅延レプリケーション

スレーブ側のW AL 適用を意図して遅らせることで、スレーブを過去の状態に維持する。

W AL 圧縮

Fu ll Page W rite 時(チェックポイント後の最初の更新時)に、W AL ファイルに書き出すフルページイメージを圧縮する。 圧縮された W AL は適用時に解凍される。

目的

注意点

データの損失がないよう、障害発生直前まで復旧できるようにする。
操作ミス対策に任意の時点まで巻き戻せるようにする。
スレーブとの同期を保証させる。

アーカイブログが領域を圧迫するため、定期的な削除が必要になる。

スレーブとの同期を保証させる。

スレーブへW AL ファイル転送ができないと、W AL が溜まり続け領域を圧迫する。

操作ミス対策に任意の時点まで巻き戻せるようにする。

スレーブは常に過去の状態であるため、参照活用には一定の制限がある。
フェイルオーバ時には遅延分の適用が必要になる。

回復不可能なデータ破壊のリスクを増やさずに、W AL の量を減らせる。

圧縮時と解凍時に通常より余分にCPU を使用する。

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

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

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

Page 13 of 96

PostgreSQL Enterprise Consortium
表 4.2 構成データ
項目

内容

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

192.168.100.101/24 server1

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

192.168.100.102/24 server2

ポート番号

5432

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

rep u ser/p assw o rd

表 4.3 ストリーミングレプリケーションの設定(マスタのp g_h b a.co n f)
設定値

内容

h o st rep licatio n rep u ser 192.168.100.101/32 md5 レプリケーション用ユーザの接続を許可する。
h o st rep licatio n rep u ser 192.168.100.102/32 md5 ・ p g_h b a.co n f は両ノード共通の設定となる事から、
またはCIDR 指定にてどちらがマスタになっても使用できる設定とする。
h o st rep licatio n rep u ser 192.168.102.0/24 md5・ IP アドレスはW AL 転送を行うネットワーク LAN を指定。
パブリック LAN 以外に、ハートビート系LAN も有力。
・認証方式は任意。

表 4.4 ストリーミングレプリケーションの設定(マスタのp o stgresql.co n f)
パラ メ ータ

設定値

内容

listen _addresses'*' または '0.0.0.0'

接続を受け入れるIP を指定する。

w al_level

rep lica(9.5 以前の場合： h o t_stan db y)

W AL に書かれる情報量を指定する。

w al_keep _segmen ts

適切な数

最低限保持するW AL ファイル数を指定する。
スロットを作成する場合は設定不要。

syn ch ro n o u s_co mmit

以下の設定が可能。

目的に応じて設定する。

remo te_ap p ly : 完全同期
o n : 同期
remo te_w rite : 準同期
lo cal : 非同期
o ff : 完全非同期

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 非同期の場合:''
同期の場合 :'*' あるいは特定ス
レーブのap p licatio n _n ame

レプリケーションの非同期と同期を切り替えに使用。
リロードで反映可能。
スレーブが1 台であれば'*' を設定。

max_w al_sen ders

スレーブ数以上を設定起動するw al sen ders 数を指定。
p g_b aseb acku p でW AL ストリーミングを指定する場合はその分も考慮

max_rep licatio n _slo t

スレーブ数以上を設定

レプリケーションスロットを作成可能な最大数

restart_after_crasho ff

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

表 4.5 ストリーミングレプリケーションの設定(スレーブのp o stgresql.co n f)
パラ メ ータ

設定値

内容

h o t_stan db yon

ホットスタンバイとして参照可能な状態で起動する。
スレーブに対しても監視SQ L を実行できるように通常は有効化する。

h o t_stan db y_feedb ack

ono n の場合、スレーブが現在処理している問い合わせについて、プライマリへ
フィードバックを送る。
通常は有効化する。特にスロットを作成する場合には有効化が必須。

Page 14 of 96

PostgreSQL Enterprise Consortium
表 4.6 ストリーミングレプリケーションの設定(スレーブの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= < アプリ名> 'マスタへの接続文字列を指定。

h o st はW AL 転送を行う LAN を指定。2 サーバ間の転送のみである事から、
ハートビートLAN でも良い。
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. 構築手順
ここでは基本的な構成における手順を記載します。
表 4.7 ストリーミングレプリケーション構成条件
項目

内容

同期式

非同期(syn ch ro n o u s_co mmit = o ff)

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

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

postgres=# CREATE ROLE repuser LOGIN REPLICATION PASSWORD 'repuser';(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
レプリケーションユーザがデータベース接続できるよう p g_h b a.co n f を次のように設定します。

$ vi $PGDATA/pg_hba.conf[pg_hba.conf]
host replication repuser 127.0.0.1/32 md5
host replication repuser 192.168.100.101/32 md5
host replication repuser 192.168.100.101/32 md5

p o stgresql.co n f
マスタとして稼働させるため、p o stgresql.co n f を次のように設定します。

Page 15 of 96

PostgreSQL Enterprise Consortium$ vi $PGDATA/postgresql.conf[postgresql.conf]
port = 5432
listen_addresses = '*'wal_level = replica
synchronous_commit = offsynchronous_standby_names = ''max_wal_senders = 3
max_replication_slots = 3
restart_after_crash = off

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

$ pg_ctl restart(3)スレーブの構築
スレーブでp g_b aseb acku p を実行し、スレーブデータベースを構築します。

$ pg_basebackup -h <マスタ IP> -p 5432 -U repuser -D $PGDATA --xlog --progress -verbose(4)スレーブの設定
p g_b aseb acku p で取得したデータベースクラスタの設定を変更し、スレーブとして稼働するようにします。 そのために取得先($PG DATA)配下で、p o stgresql.co n f を変更し、さらに reco very.co n f を作成します。
p o stgresql.co n f
次のパラメータを変更します。

$ vi $PGDATA/postgresql.conf[postgresql.conf]
hot_standby = on
hot_standby_feedback = onreco 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)スレーブの起動
スレーブを起動します。

Page 16 of 96

PostgreSQL Enterprise Consortium$ pg_ctl start

ストリーミングレプリケーションが構築され、データが同期されていることを確認します。

[マスタ側で確認]
postgres=# \xpostgres=# 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| 2017-03-15 14:29:51.283395+09
backend_xmin
| 548
state
| streaming
sent_location| 0/7000840
write_location | 0/7000840
flush_location | 0/7000840
replay_location | 0/7000840
sync_priority| 0
sync_state| async[スレーブ側で確認]
postgres=# \xpostgres=# SELECT * FROM pg_stat_wal_receiver;-[ RECORD 1 ]---------+-------------------------------------------------pid| 40723
status| streaming
receive_start_lsn| 0/7000000
receive_start_tli| 1
received_lsn| 0/7000920
received_tli| 1
last_msg_send_time| 2017-03-15 14:33:38.605953+09
last_msg_receipt_time | 2017-03-15 14:33:38.606003+09
latest_end_lsn| 0/7000920
latest_end_time| 2017-03-15 14:32:08.44417+09
slot_name|conninfo| user=repuser password=******** dbname=replication
host=<マスタのIP> port=<マスタのポート番号>
fallback_application_name=walreceiversslmode=prefer sslcompression=1

4.2.3. アー カイブモー ド運用
Po stgreSQ L は全ての変更履歴をW AL ファイルに記録しています。 データベースクラスタのクラッシュ時は、W AL に記録されている最新の変更履歴を用いることで自動復旧しています。 ただしW AL ファイルは循環使用するため、古いW AL は再利用（上書き）されます。 アーカイブモード運用により、W AL が再利用される前にアーカイブW AL ファ
イルとして保存することが可能です。 これにより、データベースクラスタに物理障害が発生した場合にも、バックアップのリストア後にアーカイブW AL ファイルと最新W AL ファイルを適用することで障害発生直前まで復旧することが可能です。
また任意の時点までデータを戻すことも可能です。PITR (Po in t In Time Reco very)という技法です。 ただしアーカイブW AL ファイルは蓄積され続け領域を圧迫するため、定期的に削除するなどの管理が必要です。

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

Page 17 of 96

PostgreSQL Enterprise Consortium
サーバ

設定ファイ ルパラ メ ータ

設定値

マスタ

p o stgresql.co n f arch ive_mo de

マスタ

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

スレーブ

reo cvery.co n f

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

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

注釈

resto re_co mman d を使用する場合、スレーブはマスタの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)必要ならばスレーブ側のreo cvery.co n f のパラメータに以下を追加する。

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

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

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

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

1. 関連パラメータ

Page 18 of 96

PostgreSQL Enterprise Consortium
既存のレプリケーションスロットの設定に加え、次のパラメータを指定します。
表 4.8 レプリケーションスロットの設定
サーバ

設定ファイ ルパラ メ ータ

設定値

マスタ

p o stgresql.co n f max_rep licatio n _slo ts スレーブ数以上

スレーブ

reo cvery.co n f

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

p rimary_slo t_n ame

内容
作成可能なレプリケーションスロット数を指定する。
使用するレプリケーションスロット名を指定する。

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

2. レプリケーションスロットの作成方法
レプリケーションスロットに関連する関数は以下の通りです。
表 4.9 レプリケーションスロットの関数
関数名p g_create_p h ysical_rep licatio n _slo t(スロット名[,
tru e/false])

説明
レプリケーションスロットを作成する。
スロット名：作成するレプリケーションスロット名を指定する。
tru e/false ： 9.6 から追加された。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 = 3$ pg_ctl restart(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)(4)スレーブ側でreco very.co n f に p rimary_slo t_n ame を加え、設定反映のため再起動する。

Page 19 of 96

PostgreSQL Enterprise Consortium$ 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)

4.2.5. 遅延レプリ ケー シ ョ ン
スレーブの適用を一時的に遅延させます。 マスタの操作ミスが即座に伝搬されるのを防ぐのが目的です。 W AL 転送は遅延なく処理されるため、データ保全の観点では問題ありません。 PITR でも過去の任意時点へ戻せますが、アーカイブ運用が必要となります。 またベースバックアップから戻す時点までW AL を適用するため、時間がかかる場
合があります。 W AL 遅延はノーアーカイブ運用でも実施可能であり、遅延している時点から W AL を適用できます。

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

設定ファイ ルスレーブ

reo cvery.co n f

パラ メ ータ

設定値

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

2. 検証(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)スレーブ側でreco very.co n f に reco very_min _ap p ly_delayを追加し、再起動する。

$ vi $PGDATA/recovery.conf[recovery.conf]
recovery_min_apply_delay = '60min'※今回は検証のため60分遅延する。

$ pg_ctl restart(2)マスタでテストテーブル1 分間隔でデータを追加する。

Page 20 of 96

PostgreSQL Enterprise Consortium

postgres=# INSERT INTO delay_test VALUES (1, localtimestamp);～約1分後～
postgres=# INSERT INTO delay_test VALUES (2, localtimestamp);
postgres=# SELECT * FROM delay_test ;
id |
time1
----+---------------------------1 | 2017-03-27 18:05:46.29221
2 | 2017-03-27 18:06:46.728155(2 rows)(3)id= 2 の更新を操作ミスとみなし、スレーブを用いて更新前へ戻す作業を開始する。
(4)マスタを停止する。

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

$ vi $PGDATA/recovery.conf[recovery.conf]#recovery_min_apply_delay = '60min'
recovery_target_time = '2017-3-27 18:06:00'$ pg_ctl restart(6)スレーブのデータでid= 2 の更新が適用されていないことを確認する。

postgres=# SELECT * FROM delay_test ;
id |
time1
----+--------------------------1 | 2017-03-27 18:05:46.29221(1 row)(7)スレーブをマスタに昇格するため設定ファイルを変更する。

$ vi $PGDATA/postgresql.conf[postgresql.conf]#hot_standby = on
#hot_standby_feedback = on
$ vi $PGDATA/recovery.conf[recovery.conf]#standby_mode = 'on'#primary_conninfo = 'host=<マスタのIP> port=<マスタのポート番号> user=repuser password=repuser'
recovery_target_timeline = 'latest'#recovery_min_apply_delay = '60min'
recovery_target_time = '2017-3-27 18:06:00'
restore_command = ''(8)スレーブを再起動し、マスタにする。

Page 21 of 96

PostgreSQL Enterprise Consortium$ pg_ctl restart(9)id= 2 の更新が適用されておらず、マスタとなったことを確認する。

postgres=# SELECT * FROM delay_test ;
id |
time1
----+--------------------------1 | 2017-03-27 18:05:46.29221(1 row)
postgres=# SELECT pg_is_in_recovery();pg_is_in_recovery------------------f(1 row)

4.2.6. WAL 圧縮
Fu ll Page W rite 時(チェックポイント後の最初の更新時)に、W AL に書き出すフルページイメージを圧縮します。 圧縮された W AL は適用時に解凍されます。 W AL ファイルのサイズが小さくなるため、書き込み や転送の時間短縮 が期待されます。 注意点として、圧縮処理および解凍処理が発生するため、通常より余分に CPU を使用します。 適用についてはそれらを総合的に判断します。 一般的な適用場面として、ストリーミングレプリケーション構成において効果的と考えられます。 以下の構成の適用を容易にします。
同期モード（転送まで）または完全同期モード（適用まで）スレーブの遠隔地配置（ディザスタ・リカバリ）

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

設定ファイ ルマスタ

パラ メ ータp o stgresql.co n f w al_co mp ressio n

設定値'o n '

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

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

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

Page 22 of 96

PostgreSQL Enterprise Consortium
表 4.12 デフォルトで出力される情報
タ イ ミ ングサイ ト

メ ッセージ

スレーブ停止時

マスタ

LO G : u n exp ected EO F o n stan db y co n n ectio n
この時のSQ LSTATE は "08P01 p ro to co l_vio latio n " です。

マスタ停止時

スレーブ

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

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
この時のSQ LSTATE は "XX000 in tern al_erro r" です。
W AL 再利用によるロス マスタ

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

ト

ERRO R: requ ested W AL segmen t 0000001100000132000000EF h as already been removed
この時のSQ LSTATE は "58P01 u n defin ed_file" です。

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
この時のSQ LSTATE は “XX000 in tern al_erro r” です。
レプリケーション操作ログ出力有効時
デフォルトで出力される情報に加えて、以下が出力されます。
表 4.13 監視強化時に出力される情報
タ イ ミ ングサイ ト

メ ッセージ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
ERRO R: requ ested W AL segmen t 0000001100000132000000EF h as already been removed
この時のSQ LSTATE は “XX000 in tern al_erro r” です。

4.3.1.3. 調査結果
エラーについては、デフォルト設定で出力があります。
エラーの監視という意味では、デフォルトで問題ないと言えます。
表の太字の文字が監視キーワード候補です。
マスターまたはスレーブを停止すると、反対側にエラー出力があります。
lo g_rep licatio n _co mman d による出力はそれほど多くなく、監視に対する影響は限定的です。
ただリロードで反映できることと合わせて、気軽に有効化できるとも言えます。
lo g_min _message = deb u g とすることでもレプリケーション関連のメッセージが出力されます。
ただし影響が大きいため、通常は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)を設定する事で確認できます。

4.3.2. 同期状況の 監視
非同期レプリケーションを構成した場合、ハードやNW 構成、利用状況によってマスターとスレーブの同期状況が変化します。
この状態を監視し、同期の遅延が許容範囲ないであるかを確認します。
■ 前提
構成 ：マスター、スレーブの2 台構成
バージョン ： Po stgreSQ L 9.6
同期モード ：問わない

4.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_xlo g_in sert_lo catio n ()関数が返す現在のW AL の挿入位置とみなします。
スタンバイのLSN は、統計情報p g_stat_rep licatio n ビューのrep lay_lo catio n を使用します。
この2 つのLSN の差分をバイト単位で表示するために、p g_xlo g_lo catio n _diff 関数の引数にそれぞれを格納しその結果が許容値を超過するかどうかで、監視を行います。

Page 23 of 96

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

#!/bin/sh
HOST=localhost
LIMIT=1
SLEEPTIME=2
ALERT="echo"function send_alert(){${ALERT} $1}
while :
doval1=$(psql -h $HOST -p 5432 -U postgres -q -t -c "select current_time;")val2=$(psql -h $HOST -p 5432 -U postgres -q -t -c "select client_addr,
pg_xlog_location_diff(master,replay_location)as
replaydiff from (select pg_current_xlog_insert_location() master)as m,
pg_stat_replication;")if [ ! -n "${val2}" ]; thensend_alert "Slave Down |${val1}"
else
byte=`echo ${val2} |awk '{print $NF}'`
if [ ${byte} -gt ${LIMIT} ]; thensend_alert "Replication Delay |${val1},${val2}"fi
fi
sleep ${SLEEPTIME}
done

監視シェルを実行中に in sert 文を実行した結果を示します。
以下の通り、遅延状況を検知でき、メールサーバ等に連携することで監視することが可能です。
また間接的に更新待ち監視も行うことができます。詳細は、更新・読取監視の項に記載します。

$ ./kanshi_No8.sh
Replication Delay | 03:03:39.701249+09, ***.***.***.*** | 104
Replication Delay | 03:03:41.725377+09, ***.***.***.*** | 104
Replication Delay | 03:03:43.745755+09, ***.***.***.*** | 104
Replication Delay | 03:03:47.790622+09, ***.***.***.*** | 56
Replication Delay | 03:03:49.813697+09, ***.***.***.*** | 56
Replication Delay | 03:03:51.834757+09, ***.***.***.*** | 56

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

■ 前提
構成 ：マスター、スレーブの2 台構成
バージョン ： Po stgreSQ L 9.6
同期モード ：同期または完全同期 (syn ch ro n o u s_co mmit = o n / remo te_ap p ly)

4.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 ビューにより間接的に、検知できないかについて検証を行いました。

Page 24 of 96

PostgreSQL Enterprise Consortium- 検証結果statemen t_timeo u t を設定しSQ L を発行しましたが、有効に機能せず
Ctrl+ C をキーインするまで停止しないという結果となりました。

[postgres@test_pg02 ~]$ 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
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 | sent_location | write_location | flush_location | replay_location | sync_priority |sync_state-------+----------+---------+------------------+----------------+----------------+-------------+-------------------------------+--------------+-----------+--------------+----------------+----------------+-----------------+---------------+-----------(0 行)(2)O S コマンドなどの外部機能により、更新処理に対して一定時間応答がない場合を監視する
timeo u t コマンドにより、指定秒数で強制切断に成功し、スタンバイと同期が取れていないメッセージが出力されます。
また終了値として、124 が返却されている為、メッセージ内容または、終了値を制御することでスレーブサーバ障害による同期待ちを検知することが可能です。

$ 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.
時間: 4995.329 ms$ echo $?
124

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

4.3.4.2. 監視方法

Page 25 of 96

PostgreSQL Enterprise Consortium
以下の監視についてまとめます。
サーバログの監視

p g_co n tro ldata コマンドによる監視
p g_co n tro l_reco very関数による監視
p g_is_in _reco very関数による監視
4.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 の変化がなく、特徴的なメッセージは出力されません。状態変更の捕捉が困難です。

4.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:pg_control last modified:

960
201608131
6356464040634181169
in archive recovery
Tue Dec 13 14:39:49 2016

～以下略～

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

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

4 種類の状態があります。
マスター/スレーブ
稼働中/停止中表 4.14 Datab ase cu 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 コマンドでリモートの状態を容易に取得できます。
注意点としては、リモート側の環境変数を認識しないため、明示的に指定する必要があります。

Page 26 of 96

PostgreSQL Enterprise Consortium
以下はローカルとリモートで同一設定の前提です。

$ ssh <remote> $PGHOME/bin/pg_controldata $PGDATA | \> grep "Database cluster state"
Database cluster state:in production

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

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

マスタ

スレーブ

min _reco very_en d_lo catio n

0/0

0/E0EAFB 50

min _reco very_en d_timelin e0

5b acku p _start_lo catio n

0/0

0/0

b acku p _en d_lo catio n

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 にマスターとスレーブの違いが表れます。
4.3.4.6. pg_is_in_recovery 関数に よる監視
リカバリ中かどうかを示します。 マスターであれば false 、スレーブであれば tru e を表示します。

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

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

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

4.4.1.2. フェ イ ルバック
Page 27 of 96

PostgreSQL Enterprise Consortium

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

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

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

Page 28 of 96

PostgreSQL Enterprise Consortium

4.4.2. 障害時運用手順■ 前提 以降の手順では次の前提とします。
Po stgreSQ L 9.6
マスタ、スレーブの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 29 of 96

PostgreSQL Enterprise Consortium
表 4.16 障害別の対処

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

スレーブ

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

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

スレーブの障害

4.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 30 of 96

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
以上でファイルオーバーは完了です。

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

PostgreSQL Enterprise Consortium
表 4.18 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 をほぼ確保できますが、スロットを指定する事でより確実になります。 スロットを使用する運用であれば、この段階で作成するのが有力です。

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

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

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

Page 32 of 96

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 33 of 96

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 34 of 96

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--- 同期
これにて、以下の構成に復旧しました。
レプリケーションスロット使用
同期式レプリケーション構成

4.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 35 of 96

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)

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

4.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 に必要な設定を記載します。
表 4.19 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 36 of 96

PostgreSQL Enterprise Consortiump g_rew in d コマンドのの主なオプションは次の通りです。
表 4.20 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 37 of 96

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 38 of 96

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

4.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 39 of 96

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
これでマスタが更新処理が可能な状態に復旧しました。 ただしシングル状態であるため、フェイルオーバと同様の作業を行います。

4.5. まとめ
4.5.1. ストリ ー ミン グレプリ ケー シ ョ ン 機能拡張の 歴史
Po stgreSQ L 9.0 でストリーミングレプリケーション機能が実装されて以降、メジャーバージョン毎にストリーミングレプリケーション関連の新機能を実装しています。以下に経緯をまとめます。

"ミスオペレーション"を"ミスオペ"と略記する場合があります。
"レプリケーションスロット"と表記した場合は物理型を指します。論理型の場合は明記します。
"レプリケーションスロット"を"スロット"と略記する場合があります。
表 4.21 ストリーミングレプリケーション機能拡張の歴史
バー
ジョ ン

分野また は目的

概要

9.1

管理性の向上
データ保護

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

9.1

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

pg_ctl promote コ マンド に よる昇格

9.1

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

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

9.1

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

pg_last_xact_replay_timestamp 関数の追加

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

同期方式に 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

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

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

[スレーブのp o stgresql.co n f]w al_receiver_statu s_in terval

スレーブにて最終適用された時間を取得する

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

PostgreSQL Enterprise Consortiumバー
ジョ ン

分野また は目的

概要

関連パラ メ ータ

9.2

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

9.2

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

pg_xlog_location_diff関数の追加

9.3

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

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

9.3

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

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 n

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

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

9.5

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

pg_rewind コ マンド の実装
昇格した新マスタと旧マスタを再同期する事で、レプリケーション構成を実装。
物理的な障害でない場合は、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

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

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

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

Page 41 of 96

PostgreSQL Enterprise Consortiumバー
ジョ ン

分野また は目的

概要

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

9.6

データ保護の多重化

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

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文で取得できる事で管理手法の選択肢が増えた。

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

4.5.2. PGEConsの 今後の 取り組み
ストリーミングレプリケーションは、以下に示す複数のテーマを包含しています。
データ保護
高可用性
参照負荷分散PG ECo n s は今後も各W G にて、さまざまな観点から Po stgreSQ L の調査・検証を続けていきます。 次期メジャーバージョンの Po stgreSQ L 10 でもレプリケーション機能の拡充が予定されています。 今後もストリーミングレプリケーションを重要分野と位置づけ、新機能の検証を継続していきたいと考えています。

Page 42 of 96

PostgreSQL Enterprise Consortium

5. Bi-Directional Replication (BDR)
5.1. はじめに
5.1.1. BDRの 特徴
5.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 では双方向にレプリケーションを実施することで、複数のサーバへの更新を可能にしています。

5.1.1.2. ユースケ ース
B DR は以下のようなケースで有用です。
1. 遠隔地や高レイテンシ環境でクラスタを構成している場合。
2. 各ノードが書き込み処理を実施する場合。
3. クラスタ間でデータが非同期であることを許容できる場合。
例としては以下のようにレスポンスを向上させるために各地でアプリケーション及びDB を動作させるようなケースが考えられます。

図 5.1 B DR ユースケース

5.1.1.3. メ カ ニズム
B DR では「 Lo gical Deco din g 」により、W AL から論理的な変更点を抽出し、 各ノードで適用することで双方向レプリケーションを実現しています。 （ Lo gical Deco din g
及びW AL の送受信はバックグラウンドワーカープロセスが実施します）
従来のトリガーを用いた双方向レプリケーションの場合、 下図のように書き込みが余分に発生（変更記録、変更反映）してしまいます。

図 5.2 トリガベースメカニズム
一方、B DR では余分な書き込みが発生せずパフォーマンス的に有利となっています。

図 5.3 B DR メカニズム
利用されているPo stgreSQ L のメカニズムの一覧です。

Page 43 of 96

PostgreSQL Enterprise Consortium
表 5.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 機能によって読み出されることを想定している。

5.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」で確認可能です。

図 5.4 結果整合性

5.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 の実行に失敗してしまいます。

図 5.5 グローバルシーケンス

2. ステップ/オフセットシーケンス
ステップ/オフセットシーケンスでは、各ノードで通常のPo stgreSQ L シーケンスを使用します。 各シーケンスを同じ量だけ増分されるようにし、値が重複しないように設定します。

Page 44 of 96

PostgreSQL Enterprise Consortium
設計時には注意が必要です。
増分する値をある程度大きく取らないとノードの追加に対応できません。（例： 10 の増分であればノード数の追加は10 台まで）

図 5.6 ステップ/オフセットシーケンス

5.1.1.6. 比較表
類似機能及び製品との机上比較の結果です。
表 5.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/
5.1.1.7. サポート
サポートについては以下が存在します。
表 5.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 /5.2. 調査、検証の目的
机上の情報整理および検証について、以下を主な目的としています。
情報整理

B DR を使用したマルチマスタ環境を構築、運用するにあたり、各種機能やパラメータの理解に必要な情報の提供
検証

B DR の動作（競合時等）や障害発生時の対処法を調査
更新性能の検証5.3. 調査、検証の前提
Page 45 of 96

PostgreSQL Enterprise Consortium
表 5.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
以下は構成図になります。

図 5.7 調査、検証環境構成図

5.4. BDR環境構築時の設定と設定手順
5.4.1. BDR環境の 設定手順
5.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
こちらの環境 で検証を実施しました。

5.4.1.2. 対象試験サーバ
表 5.5 対象試験サーバ
サーバ名

IP アド レスn o de1

192.168.0.10

n o de2

192.168.0.12

5.4.1.3. 事前確認

Page 46 of 96

PostgreSQL Enterprise Consortium

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

以下のコマンドでインストール済み一覧を確認します。

# yum list installed postgresql-bdr94-2ndquadrant-redhat.noarch 0:1.0-3[省略]
インストール済みパッケージ

postgresql-bdr94-2ndquadrant-redhat.noarch

5. B DR インストール
登録した B DR レポジトリから B DR をインストールします。

Page 47 of 96

1.0-3

installedPostgreSQL Enterprise Consortium# 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 検証用に新規でデータベースクラスタを作成します。

$ 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)をします。

Page 48 of 96

PostgreSQL Enterprise Consortium$ 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. データベース作成
B DR の動作確認用のデータベースを作成します。

$ createdb bdrtest

以下のコマンドで作成したデータベースへの接続を確認します。

Page 49 of 96

PostgreSQL Enterprise Consortium$ 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 50 of 96

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 51 of 96

PostgreSQL Enterprise Consortium$ psql -U postgres bdrtest=# SELECT * FROM t1bdr;
c1
---1
2
(2 行)
=# INSERT INTO t1bdr VALUES (3);5.5. BDR動作検証
5.5.1. ノ ー ドの 追加・ 切り離し
5.5.1.1. 検証の目的
ノードの追加/切り離しをオンライン(DB 停止)なしで実行できるか否かを確認します。

5.5.1.2. 検証内容
本検証では、2 台で構成された B DR クラスタに対して、下記を実施しました。

1. ノード切り離し
2. ノード追加「ノード切り離し」および「ノード追加」時に他ノード(上図のn o de1)にトランザクションを実行し、トランザクションにエラーが発生するか否かを確認しました。

5.5.1.3. 検証環境
こちらの環境 で検証を実施しました。

5.5.1.4. 検証手順
5.5.1.4.1. ノ ード 切 り 離 し

1. B DR の各ノード状態確認
B DR の各ノード間のレプリケーションが正常に動作していることを「 b dr.b dr_n o des 」テーブルを用いて確認します。

Page 52 of 96

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 53 of 96

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 にエラーが発生してないことを確認します。本検証ではエラーは発生しませんでした。

5.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
表 5.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 54 of 96

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 55 of 96

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 56 of 96

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 57 of 96

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から作成されていることを確認

5.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で終了しました

ノード追加にはプロセスの再起動は必要ありませんが、トランザクションの停止が必要でした。

5.5.2. グロー バル シ ー ケン ス設定
5.5.2.1. 検証の目的
B DR に実装されたグローバルシーケンスの利用方法について確認します。 グルーバルシーケンスを利用することでノード毎に払い出されるシーケンス番号を独立させ、ノード間のシーケンス番号の競合を防ぐことができるか否かを確認します。

5.5.2.2. 検証環境
こちらの環境 で検証を実施しました。

5.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 58 of 96

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 59 of 96

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に割り振られる。

5.5.2.4. 検証結果
グローバルシーケンスを用いることでノード間のシーケンス番号の競合を抑止できることが確認できました。 グルーバルシーケンスについてはいくつかの制限事項がマニュアルに記載されておりますので、注意して下さい。

1. 1 （デフォルト値）のINCREMENTだけがサポートされています。

Page 60 of 96

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

5.5.3. 選択的レプリ ケー シ ョ ン
選択的レプリケーションについて記載します。

5.5.3.1. 検証目的
テーブル単位での選択的レプリケーションの可否を確認します。 選択的レプリケーションの可否は互いのデータベース間での任意のテーブルのデータ状態を元に判断します。
また、選択的レプリケーションを実現する際の変更手順を明確にする事を目的とします。

5.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)の動作確認

5.5.3.3. 検証環境
こちらの環境 で検証を実施しました。

5.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 61 of 96

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 62 of 96

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 63 of 96

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 64 of 96

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 行)<-- レプリケーションされていないこと

5.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. レプリケーションセットにテーブルを追加した場合も以前のデータ内容はノードに同期されません。 通常、管理者は、テーブルをレプリケーションセットに追加した後、手動で同期する必要があります。

5.5.4. 更新処理競合時の 動作
5.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. 競合発生時にシステムカタログに記録される情報
5.5.4.2. 検証内容

B DR 動作検証概要図

Page 65 of 96

PostgreSQL Enterprise Consortium
下表の競合発生時の動作を検証しました。
表 5.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

その他

自動的に解決出来ないデータの相違が発生した場合に手動で調整を行う方法を検証します。

5.5.4.3. 検証環境
こちらの環境 で検証を実施しました。

5.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 66 of 96

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);5.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 67 of 96

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 68 of 96

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 69 of 96

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 70 of 96

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 71 of 96

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 72 of 96

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 73 of 96

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 74 of 96

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 : 変更を無視し、破棄された

5.5.4.4.2. 外 部 キー制 約

1. 外部キー制約の競合■ 競合概要1 つのノードで外部キー制約が定義されたテーブルに対してデータを挿入し、上記データ挿入がもう片方のノードに反映される前に、 もう片方のノードにおいて外部キーの参照先であるテーブルのデータを削除することで、外部キー制約が定義されたテーブルへのデータ挿入と、外部キー参照先テーブルに対するデータ削除を競合させます。

Page 75 of 96

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テーブルに存在する。

5.5.4.4.3. 排 他 制 約

1. 排他制約の競合■ 競合概要排他な関係である2 つのデータを異なるノードから同時にテーブルに INSERTし、ノード間のデータに競合が発生するケースを検証します。

■ 検証結果一つのノードでINSERTされたデータをもう片方のノードに反映させる際に排他制約違反が発生し、ノード間の接続が切断されます。 競合を解消するた
めにはオペレータでのデータ操作が必要となるため注意が必要です。

■ 競合発生手順
下記の排他制約を持つテーブルを作成します。

Page 76 of 96

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 77 of 96

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 78 of 96

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 行)

5.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 79 of 96

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 行)

5.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 80 of 96

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;

5.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 81 of 96

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 82 of 96

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;5.5.4.5. 検証結果
一部の競合パターンにて、意図しない動作が発生するため、現段階では競合が発生しないパターンで利用すべきと考えます。

5.5.5. ノ ー ド障害と復旧
5.5.5.1. 検証の目的
複数ノードで構成されるクラスタ環境内の1 ノードに障害が発生した場合でも、他ノードで継続利用可能か否かを確認します。 また、障害が発生したノードをクラスタ環境に復旧させる手順を確認します。

5.5.5.2. 検証内容
本検証では2 台で構成された B DR クラスタに対して、下記を実施しました。

1. ノード障害
2. ノード復旧

5.5.5.3. 検証環境
こちらの環境 で検証を実施しました。

5.5.5.4. 検証手順
5.5.5.4.1. ノ ード 障 害

1. B DR の各ノード状態確認
B DR の各ノード間のレプリケーションが正常に動作していることを「 b dr.b dr_n o des 」テーブルを用いて確認します。

Page 83 of 96

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 84 of 96

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 85 of 96

PostgreSQL Enterprise Consortium
手順1 で実行した p gb en ch にエラーが発生してないことを確認します。本検証ではエラーは発生しませんでした。

5.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
表 5.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 86 of 96

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 87 of 96

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 88 of 96

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から作成されていることを確認

5.5.5.5. 検証結果
ノード障害が発生した際にも、他のノードではトランザクションを継続実行することが可能でした。 障害ノードを復旧させるためにはデータ操作が禁止されているシステムカタ
ログのデータ削除が必要でした。

5.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

5.6. BDR性能検証
5.6.1. 更新性能検証結果
5.6.1.1. 検証目的
本試験はB DR の本来のユースケースである、 高レイテンシ環境との双方向レプリケーション環境における更新クエリのレスポンス改善とそれに伴う性能改善を確認したものです。

5.6.1.1.1. 検 証 内 容
今回は 2014 年度検証報告書 （可用性編） に記載された東京-シンガポール間の回線情報（応答速度76.95ms 、帯域幅0.16G b its/s ）をもとに、 以下のような環境を構築しp gb en ch を実行しました。

Page 89 of 96

PostgreSQL Enterprise Consortium

通常のストリーミングレプリケーションであれば、 更新クエリを実行したい場合はシンガポールのマスタノードに更新要求を実施する必要がありますが、 B DR では最寄りのノードに対して更新要求を実施することが可能です。

5.6.1.1.2. 検 証 環 境
こちらの環境 で検証を実施しました。

5.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 90 of 96

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

帯域制限の結果以下のような環境になります。
表 5.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 91 of 96

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

5.6.1.1.4. 検 証 結 果
本検証では以下を確認することが出来ました。
レスポンスタイムの低減

Page 92 of 96

PostgreSQL Enterprise Consortium
低レイテンシのサーバに更新クエリを実行可能であるため、レスポンスタイムを低減することが出来ました。

TPS の向上
レスポンスタイムが低減されたため、TPS の向上が見られました。
"17.057455" -> "92.602290"（約5.4 倍）
ただし、本検証では片系のみに変更を実施し競合が発生しないようにした試験だったため、このような結果になったものと思われます。
本検証で使用しているp gb en ch （ TCP-B ライク）のような、現在の値に対して加算していくような処理の場合、結果整合で競合を解決することができないため、 絶対に競合が発生しない構成が必要です。
例）更新するテーブルを拠点ごとに分ける等

5.7. まとめ5.7.1. BDR検証まとめ
本検証では、B DR の機能や特徴および主なユースケースを机上調査を実施した上で、B DR の動作検証および性能検証を実施しました。 本検証で実施した B DR の動
作検証の結果は下表の通りです。
表 5.10 結果評価基準
記号

意味

○

問題なし。

△

利用時に問題になるケースがある。

×

対応していない。事実上使えない。
表 5.11 B DR 検証結果
項番

検証概要

結果

補足

1

ノード追加/削除△

ノード削除はオンラインで実行可能。ノード追加時にトランザクションの停止が必要。 また、削除したノードを追加する場合にはシステムカタログの操作が必要。

2

グローバルシーケンス○

シーケンスの競合を防ぐことが可能。ただし、マニュアルに記載された制限事項については確認が必要。

3

選択的レプリケーション○

B DR を利用して任意のテーブルのデータ集約等を実現可能

4

更新処理競合時の動作△更新が競合するパターンで意図しない動作が発生し、競合解消のため手動での操作が必要。

5

ノード障害と復旧△

ノード復旧時にはトランザクションの停止が必要。 また、障害が発生したノードを復旧させる際にシステムカ
タログの操作が必要。

本検証で利用したバージョン(1.0.2)では更新が競合するパターンで意図しない動作が発生するため、B DR を適用する場合、競合が発生しないようなアプリケーション設計
やテーブル設計が必要になります。
例) 更新するテーブルを拠点ごとに分ける等
ノード障害時の運用においても、一般的でないシステムカタログの操作を必要とするといった今後の改善が必要と思われる結果が確認されました。
また、B DR の選択的レプリケーションを用いることで、Po stgreSQ L のストリーミング・レプリケーションでは実現できない、テーブル単位のレプリケーションが実現可能なことを確認できました。 上記機能を用いて、システム間のデータ連携(データ集約等)を柔軟に実現できると考えております。
性能検証の結果より、ユースケースで想定している「遠距離拠点間で双方向に更新する」場合に、レスポンスタイムの低減と処理向上につながることが確認できました。
机上で調査した通り、B DR を適用することで「遠距離拠点間のトランザクション性能改善」や選択的レプリケーション機能を利用した「柔軟なデータ連携」を実現できると考えます。

5.8. 参考文献[B DR] Po stgres-B DR ドキュメント h ttp ://b dr-p ro ject.o rg/do cs/stab le/5.8.1. 関数一覧

Page 93 of 96

PostgreSQL Enterprise Consortium
表 5.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

5.8.2. シ ステム カタログ一覧
表 5.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 94 of 96

PostgreSQL Enterprise Consortium

6. まとめ本文書では、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 コマンドを使うことで、これまで必要とされていたフルバックアップ取得を不要とする運用が可能になっていることを確認できました。

B DR は、現状のストリーミングレプリケーションでは不可能なマルチマスタ構成に対応することから、シングルマスタ構成に起因する課題解消が期待されます。今回の検証では、B DR の特徴、ユースケース、メカニズムを類似機能、製品と机上比較した上で、更新処理競合時の動作、ノード障害時の動作、更新性能についてそれぞれ検証しました。更新性能の検証結果より、ユースケースで想定している「遠距離拠点間で双方向に更新する」 場合に、レスポンスタイムの低減とTPS 向上につながることがわかりました。
一方で、現時点のバージョンでは更新が競合するパターンで意図しない動作が発生するため、現段階では競合が発生しないパターンで利用すべきと考えます。そこで、
B DR の利用時は競合が発生しにくいテーブル設計にすることが望ましいです。また、ノード障害時の運用においても、一般的でないシステムカタログの操作を必要とするといった今後の改善が必要と思われる結果が確認されました。
これらの結果から、B DR についてはユースケースと実際の利用用途が合致しているかを見極めた上で利用するかどうかを判断する必要があるといえます。
今回の検証結果がストリーミングレプリケーション構成を実際に利用している方々の運用改善につながること、また B DR を利用すべきかどうかを判断する際の参考情報として活用いただけることを期待しています。

Page 95 of 96

PostgreSQL Enterprise Consortium

7. 著者（企業・団体名順）
版
第1.0 版(2016 年度W G 3)

所属企業・ 団体名

部署名

氏名

株式会社アシスト

データベース技術本部

竹内 尚也

株式会社アシスト

データベース技術本部

柘植 丈彦

株式会社オージス総研

プラットフォームサービス本部 IT基盤技術部

大西 斉

TIS 株式会社

IT基盤技術本部 IT基盤技術推進部

中西 剛紀

日本電信電話株式会社

オープンソースソフトウェアセンタ

坂田 哲夫

株式会社富士通ソーシアルサイエンスラボラトリ

プラットフォームインテグレーション本部 第四システム部

小山田 政紀株式会社富士通ソーシアルサイエンスラボラトリ

プラットフォームインテグレーション本部 第四システム部

高橋 勝平

株式会社富士通ソーシアルサイエンスラボラトリ

プラットフォームインテグレーション本部 第四システム部

香田 紗希

Page 96 of 96