# --------------------------------------
# ポストグレスメンテナンス
# --------------------------------------

PostgreSQLは追記型のデータベースです。
updateやdeleteを行っても物理的にデータが削除されるわけではありません。
不要になったデータを削除するためにはvacuum処理が必要です。
vacuum処理には下記の2種類があります。
スーパーユーザ(postgresなど)で行ってください。

PostgreSQL の VACUUM コマンドは以下の理由より定期的に実行させる必要があります。

・更新、あるいは、削除された行によって占められたディスク領域の復旧。
・PostgreSQL 問い合わせプランナによって使用されるデータ統計情報の更新。
・トランザクション ID の送り込みによる非常に古いデータの損失を防止。

上述の理由それぞれを目的として実行される VACUUM の頻度や適用範囲は
インストレーションに対する要求がどのようなものかによって変わります。
従って、データベース管理者はこれらの問題を理解し、適切な保守計画を構築しなければなりません



# --------------------------
# 通常のバキューム処理
# --------------------------
このバキューム処理はデータを実際に削除するのではなく、再利用のための管理テーブルをメモリ上に作成します。

# $ vacuumdb -a -z

【備考】
データベースを止める必要はありませんが、負荷が大きいので通常はLinuxのcronを使って深夜など利用が少ない時に毎日行います。

# --------------------------
# フルバキューム処理
# --------------------------

このバキューム処理は実際にデータベース内の不要レコードを削除します。

# $ vacuumdb -a -f

【オプション】
-a 全てのデータベースに対して行います。
-f バキュームfullモードで行います。

【備考】
データベースへのアクセスが全く出来なくなりますので、月に1度の保守のような時に実施します。
通常のバキュームが正常に行われていれば必要ないのですが、削除が多かった時などは再利用テーブルを
オーバーする場合があります。この場合の不要レコードはフルバキュームでしか削除できません。


# --------------------------
# クーロン
# --------------------------

・この処理を行う時間帯
VACUUM は、普通のデータベース操作と並行して実行できるそうです。
そのため、定常的なバキューム処理は1日のうちで使用頻度が低い時間にスケジューリングする必要はないそうです。

・クーロンへの設定方法
(ユーザー:postgresで)
$ crontab -e

設定内容(毎日0時0分に実行する例):
0 0 * * * $PGSQL/bin/vacuumdb --all




# -----------------------------
# 詳しい解説
# -----------------------------

【ディスク容量の復旧】
通常の PostgreSQL の操作では、行の UPDATE もしくは DELETE は古い タプル(あるバージョンの行)を即座に削除しません。
この方法は、マルチバージョン同時性制御 の恩恵を受けるために必要なものです。タプルは他のトランザクションから参照される可能性がある場合は削除されてはなりません。
しかし、結局は、更新される前のタプルや削除されたタプルを参照するトランザクションはなくなります。
必要なディスク容量が無限大にならないように、これらが占める領域は、新しいタプルで再利用できるように回収されなければなりません。
これは VACUUM を実行することで行なわれます。

頻繁に更新、削除されるテーブルは、滅多に更新されないテーブルよりもより頻繁にバキュームを行なう必要があります。
変更頻度がないことが分かっているテーブルを除く、限定したテーブルに対してバキュームを行なう定期的な cron 処理を設定することは有益なものになるかもしれません。
これは、巨大な、更新頻度が高いテーブルと巨大な更新頻度が低いテーブルの両方が存在する場合にのみ有益です。
小さなテーブルのバキューム処理のコストは考慮する必要はありません。

標準形式の VACUUM は、ディスク容量を安定状態の使用量のレベルで維持することを目的に最もよく使用されます。
標準形式では、古いタプルを探し、その領域をそのテーブル内で再利用できるように変更します。 しかし、テーブルファイルを縮小させ、オペレーティングシステムに
ディスク容量を返却するといった難しいことは行ないません。ディスク容量をオペレーティングシステムに返却する必要がある場合は、
VACUUM FULL コマンドを使用して下さい。 しかし、すぐに再度割り当てる必要があるディスク容量をリリースするポイントは何でしょうか?
更新頻度の激しいテーブルの保守においては、不定期の VACUUM FULL よりも適切な頻度で標準の VACUUM の方がよりよい方法です。

ほとんどのサイトで推奨できる方法は、データベース全体の VACUUM を 1 日 1 回使用頻度が低い時間帯にスケジュールすることです。
必要ならば、更新頻度の激しいテーブルのバキューム処理をより頻繁に行なうよう追加して下さい。
(1 つのインストレーションで複数のデータベースがある場合、それぞれをバキュームすることを忘れないで下さい。vacuumdb スクリプトが役に立つかもしれません。)
容量の復旧のための定常的なバキューム処理には、VACUUM FULL ではなく、普通の VACUUM を使用して下さい。

VACUUM FULL は、テーブル内のほとんどのタプルを削除したことが判明している場合に推奨します。
その安定状態のテーブルサイズをVACUUM FULL のより積極的な方式によって大いに縮小できるからです。
テーブルの内容が度々完全に削除される場合、DELETE の後にVACUUM を使用するよりも、TRUNCATE を使用する方が良いでしょう。

【プランナ用の統計情報の更新】
PostgreSQL 問い合わせプランナは、優れた問い合わせ計画を作成するのに、テーブルの内容に関する統計情報に依存しています。
この統計情報は ANALYZE によって収集されます。 このコマンドはそのものを呼び出す以外にも、VACUUMのオプション処理としても呼び出すことができます。
合理的な精度の統計情報を持つことは重要です。 非効率的な計画を選択してしまうことはデータベース性能を悪化させてしまいます。
領域復旧のためのバキューム処理と同様、頻繁な統計情報の更新は、滅多に更新されないテーブルよりも更新の激しいテーブルにとってより有益です。
しかし、頻繁に更新されるテーブルであっても、データの統計的な分布が大きく変更されなければ、統計情報を更新する必要はありません。
単純な鉄則は、テーブル内の列の最小値、最大値にどのくらいの変化があったかを考えることです。例えば、行の更新時刻を保持する timestamp 列の場合
最大値は行が追加、更新されるに連れて、単純に増加します。 こういった列は、おそらく、例えば、ある web サイト上のアクセスされたページの URL を保持する列よりも
頻繁に統計情報を更新する必要があるでしょう。この URL 列の更新頻度も高いものかもしれませんが、その値の統計的な分布の変更は相対的に見ておそらく低いものです。

特定のテーブルに対して ANALYZE を実行することができます。 また、テーブルの特定の列のみに対してさえも実行することができます。 ですので、
アプリケーションの要求に応じて、他よりも頻繁に一部の統計情報を更新できるような柔軟性があります。
しかし、実際は、この機能は有用ではないかもしれません。PostgreSQL 7.2 から、ANALYZE は、全ての行を読むのではなく、
テーブルからランダムに行を抽出して統計処理を行なうようになったため、巨大なテーブルに対してもかなり高速に処理するようになりました。
ですので、頻繁にデータベース全体に対して実行する方が、おそらくかなり単純になります。
Tip: 列単位での ANALYZE 実行頻度の調整は非常に実用的とはいえるものではありませんが、列単位の調整を ANALYZE で集計される統計情報の詳細レベルで
行なうことは価値がある場合があります。 WHERE 句で良く使用され、データ分布の規則性がほとんどない列は、他の列よりもより細かいデータの度数分布が必要になるでしょう。
ALTER TABLE SET STATISTICS を参照して下さい。

ほとんどのサイトで推奨できる方法は、1 日 1 回使用頻度の低い時間帯に、データベース全体に対してANALYZE をスケジュールすることです。 通常は、毎晩の VACUUM と組み合わせることができます。しかし、テーブルの統計情報の変更が相対的に遅いサイトでは、過剰であるかもしれません。 より低い頻度で ANALYZE を実行することで十分です。


【トランザクション ID の送り込み失敗の防止】
PostgreSQL の MVCC トランザクションのセマンティックは、トランザクション ID(XID) 番号の比較が可能であることに依存しています。
現在のトランザクションの XID よりも新しい挿入時の XID をもったタプルは、現在のトランザクションから可視であってはなりません。
しかし、トランザクションID のサイズには制限 (執筆時点では 32bit) があり、長時間 (40 億トランザクション)稼働しているインストレーションは
トランザクションの送り込みを経験します。XID のカウンタが一周して 0 に戻り、そして、突然に、過去になされたトランザクションが将来のものとみえるように
つまり、その出力が不可視になります。端的にいうと、破滅的なデータの損失です。(実際はデータは保持されていますが、それを入手することができなければ、慰めにならないでしょう。
PostgreSQL 7.2 の前まででは、XID の送り込みから保護する唯一の方法は最低でも 40 億トランザクション毎に initdb を再度行なうことでした。
当然これは、トラフィックが多いサイトを十分に満足させることはありませんでしたので、より良い解決方法が案出されました。
この新しい方法では、インストレーションを、initdb や再起動などなく、限界なく稼働状態とすることができます。
この保守要求の代価は、データベースの各テーブルは、最低でも 10 億トランザクション毎にバキュームされなければならない、ということです。

実際、これは面倒な要求ではありませんが、失敗の結果は(ディスク容量の浪費や性能の低下ではなく)完全なデータの損失となりますので、
データベース管理者が、直前の VACUUM からの経過時間を保持できるような少し特別な準備を行ないました。この節の残りで詳細を説明します。

XID の新しい比較方法では、2 つの特殊な XID を区別し、1 と 2 と番号を付けます。 BootstrapXID と FrozenXID)。
この 2 つの XID は常に全ての通常の XID よりも古いものとみなされます。通常の XID (2 以上の値) は modulo-231 という数式を使用して比較されます。
これは、全ての通常の XID では、20 億の "より古い" XIDと 20 億の "より新しい" XID が存在することを意味します。
言い替えると、通常の XID 空間は終ることなく循環されているということです。そのため、ある特定の XID でタプルを作成すると、そのタプルは、以降の 20 億トランザクションからは
どの通常の XID について話しているのかには関係なく、 "過去のもの" と認識されます。そのタプルが 20 億トランザクション以上後にも存在していた場合、
それは突然に未来のものとして認識されます。このデータ損失を防ぐために、20 億トランザクションより古いとみなされるより、
少し前に古いタプルの XID を FrozenXID に再割り当てする必要があります。この特殊な XID に割り当てられた後は、送り込み問題に関係なく、
全ての通常のトランザクションから "過去のもの" として認識され、また、そのタプルはどれだけ古いものであろうと、削除されるまで好ましい状態となります。
この XID の再割り当ては VACUUM で扱われます。

VACUUM の通常のポリシーは、過去の 10 億トランザクションより古い通常の XIS を持つタプルを全て FrozenXID に再割り当てすることです。
このポリシーは元々の挿入時の XID をどこからも参照されることがなくなるまで、保存します。
(実際は、ほとんどのタプルはおそらく "凍結" になることなく、生成、削除されるでしょう。)このポリシーでは、任意のテーブルの VACUUM の最大の安全な間隔は
正確に 10 億トランザクションです。 この値以上の間行なわなかったとすると、前回は再割り当てするほど古くなかったタプルが 20 億トランザクション以上の古さとなってしまい
未来のものとして循環され、失われてしまいます。(もちろん、その後の 20 億トランザクション後に再度出現しますが、これは何の助けにもなりません。)
周期的な VACUUM は、これまで説明してきた理由により、とにかく必要とされます。 10 億トランザクションの間バキュームされないテーブルがあるとは考えられません。
しかし、管理者がこの制約に合っていることを確実にすることができるように、VACUUM は pg_database システムテーブルにトランザクション ID 統計情報を保存します。
特に、データベースの pg_database 行の datfrozenxid フィールドは、データベース全体に対するバキューム操作
(つまり、特定テーブルの指定のない VACUUM) が完了した時に更新されます。このフィールドに保存された値は、VACUUM コマンドで使用された、凍結用の切捨て XID です。
この切捨て XID よりも古い、全ての通常の XID はそのデータベースの FrozenXID によって置換されていることが保証されています。
この情報を検査する簡便な方法は、以下の問い合わせを実行することです。

SELECT datname, age(datfrozenxid) FROM pg_database;
age 列は切捨て XID から現在のトランザクション XID までのトランザクション数を測ります。

標準の凍結ポリシーでは、よくバキュームされたデータベースでのage 列は 10 億から始まります。
age が 20 億に近い場合、そのデータベースは、送り込み問題の危険性を回避するために、再度バキュームされなければなりません。
推奨する方式は、十分安全なマージンを確保するために、各データベースを少なくとも 0.5 億 (500万) トランザクション毎にバキュームすることです。
この規則に合わせることを補助するために、各データベース全体に対する VACUUM は、1.5 億トランザクション以上の age を示す pg_database のエントリがあった場合に
自動的に警告を発します。

play=# vacuum;
WARNING:Some databases have not been vacuumed in 1613770184 transactions.
Better vacuum them within 533713463 transactions,
or you may have a wraparound failure.
VACUUM

FREEZE オプション付きの VACUUM は、全ての開いているトランザクションによって適切とみなすことができるほど古いタプルを凍結するという、
より積極的な凍結ポリシーを使用します。特に、VACUUM FREEZE が他の処理は待たせる状態のデータベースで行なわれた場合、そのデータベース中の 全ての タプルは凍結されます。
従って、データベースが全く変更されない限り、トランザクション ID の送り込みを防ぐことを目的としたバキューム処理を今後行なう必要はなくなります。
この技術は、initdb において template0 を準備するために使用されています。また、これは、pg_database にて datallowconn = false と記録されたユーザ作成の
データベースの準備の時にも使用しなければなりません。 このデータベースに接続することができませんので、バキュームする方法が存在しないからです。
バキュームされないデータベースに関する、VACUUM の自動警告メッセージはこの種のデータベースに対する間違った警告を防ぐために datallowconn = false の付いた
pg_database エントリを無視することに注意して下さい。 従って、この種のデータベースを正確に凍結させておくことは、ユーザの責任となります。