はじめに
前回まででPostgreSQLのレプリケーションまでは完了しました。今回はレプリケーション側でのpg_dumpallでのバックアップ方法と、レプリケーションでの運用時の注意点について解説してみたいと思います。
pg_dumpallでのバックアップ
データベースをレプリケーションしているのであれば、マスター側の負荷を下げる事もできますしレプリケーション側で日次バックアップ等を取りたくなると思います。また、プログラムの改修によるデータベースの変更するといった場合にレプリケーションの同期を止める事でバックアップを取得する時間を減らすといった事もできます。
但し、そのままではpg_dumpallではバックアップできませんのできちんとした手順が必要です。
そのままpg_dumpallを実行するとどうなるのか?
実際に何もせずにpg_dumpallを行うと、
DETAIL: User query might have needed to see row versions that must be removed.
この様なエラーが出てバックアップ処理が停止してしまいます。
これはレプリケーション側がpg_dumpall時点のデータの状態を参照し続けようとするのですが、プライマリから不要になったデータを削除しようとする要求(WAL)が衝突している状態が発生しているためです。pg_dumpallは少しの間であれば整合性が保たれる状態になるまで待つのですが、何時まで経っても新たな要求が来るため、最終的にはpg_dumpallはキャンセルされてしまいます。
どのように回避すべきか?
回避するには主に3つの方法があります。
1. 待機時間を延ばす
レプリケーション側のconfファイルで max_standby_streaming_delay の値を伸ばすことで衝突しなくなるまで待機させる事ができます。但し、更新が多いデータベースでは結局何時まで経ってもバックアップが進まないという事が発生する可能性があります。
2.フィードバックを送る
レプリケーション側からプライマリ側へ今はVACUUMしないように伝えて衝突が発生しないようにする方法です。設定はレプリケーション側のconfに hot_standby_feedback = on とするだけですので手軽に出来る方法ではありますが、プライマリ側のVACUUM処理がバックアップ中は停止する為、ストレージサイズが肥大化する等のプライマリ側への影響があります。
3.レプリケーションスロットの使用
レプリケーション側の同期を一時的に停止し、送られてくるWALをその間レプリケーション側で一時的に保存しておく方法です。同期が停止している間にバックアップを行い、完了後に再開させることで一時的に保存しておいたWALを随時反映させて再度同期させます。
一番容易に回避するには2のフィードバックを送る方法でしょうか。pg_dumpall もマスター側で行っている運用と同じ様にする事ができます。但し、既に本番で運用中だったり非常にトランザクションが多いような場合、あまりマスター側は触りたくない、影響を与えたくないような場合は、3のレプリケーションスロットを使う方法がよいと思います。
レプリケーションスロットを使ったバックアップ
レプリケーションスロットを使ってバックアップを取るには、
-
WALの反映を止める(WALを保持しておく)
-
バックアップを取得する
-
WALの反映を再開する
という手順が必要になります。
1.WALの反映を止める
停止するには、SELECT pg_wal_replay_pause(); を実行します。
停止したかの確認は、SELECT pg_is_wal_replay_paused(); で結果が t となれば停止、f であれば停止していない、実行中ということになります。
2.バックアップを取得する
通常通りに pg_dumpall を実行します。
3.WALの反映を再開
再開するには、SELECT pg_wal_replay_resume(); を実行します。
バックアップスクリプトに組み込めば問題ないレベルですね。
その他に注意すべき点
小規模であれば以上の設定で殆どの場合は問題ないと思いますが、規模が大きい、データーの更新が頻繁に行われる、あるはバッチ処理による大量のデータの更新が行われているようなデータベースの場合、いつの間にかレプリケーションが停止する場合があります。
FATAL: could not receive data from WAL stream: ERROR: requested WAL segment 000000010000132A00000082 has already been removed
これはレプリケーション側が次に必要なWALデータを要求したのにマスター側が既にそのWALデータを削除してしてしまっている状態です。マスター側のデータ更新が早すぎてレプリケーション側が追い付いていないような状態といえます。
この問題を解決するにはマスター側で保持するWALの量を多くするしか無く、主に max_wal_size、min_wal_size、wal_keep_size あたりを調整する必要があります。
| パラメータ | ディフォルト値 | 役割 |
| max_wal_size | 1GB | WALがこのサイズを超えると「チェックポイント」を発生させて古いログを削除します |
| min_wal_size | 80MB | WALを削除せず次に使う為のリサイクルして残しておく最低限の量です |
| wal_keep_size | 0 | レプリケーションの為に最低限これだけは残しておくという猶予分です |
ディフォルトの値は非常に小さく、レプリケーションについては全く考慮されていません。ただ、この設定値が正しいという事も一概には言えず、実際のデータベースの規模等を見て調整する必要があるでしょう。
幸いこれらの数値はPostgreSQLを停止せずとも変更する事ができます。
最後に
実際に運用を行っていく上では検討すべきことがありますが、もし同期が止まってしまっても一旦レプリケーション側のデータを削除して、pg_basebackup すれば復旧は容易です。また、バックアップだけでなく、他の使い方なんかも考えられるかもしれませんね。

