★PostgreSQLカンファレンス2024 12月6日開催/チケット販売中★
他のバージョンの文書 16 | 15 | 14 | 13 | 12 | 11 | 10 | 9.6 | 9.5 | 9.4 | 9.3 | 9.2 | 9.1 | 9.0 | 8.4 | 8.3 | 8.2 | 8.1 | 8.0 | 7.4 | 7.3 | 7.2

25.1. 定常的なバキューム作業 #

PostgreSQLデータベースはバキューム処理として知られている定期的な保守を必要とします。 多くのインストレーションでは、25.1.6で説明されている自動バキュームデーモンでのバキューム処理を行わせることで充分です。 それぞれの状況に合った最善の結果を得るため、そこで説明する自動バキューム用パラメータの調整が必要かもしれません。 データベース管理者によっては、cronもしくはタスクスケジューラスクリプトに従って典型的に実行される、手作業管理のVACUUMコマンドによりデーモンの活動を補足したり、置き換えたりすることを意図するかもしれません。 手作業管理のバキューム処理を適切に設定するためには、以下のいくつかの小節で説明する問題点を理解することが必須です。 自動バキューム処理に信頼をおいている管理者にとっても、この資料に目を通すことはそれらの理解と自動バキューム処理の調整に役に立つことでしょう。

25.1.1. バキューム作業の基本 #

PostgreSQLVACUUMコマンドは以下の理由により定期的にそれぞれのテーブルを処理しなければなりません。

  1. 更新、あるいは削除された行によって占められたディスク領域の復旧または再利用。
  2. PostgreSQL問い合わせプランナによって使用されるデータ統計情報の更新。
  3. 可視性マップの更新。 これによりインデックスオンリースキャンが高速化される。
  4. トランザクションIDの周回またはマルチトランザクションIDの周回による非常に古いデータの損失を防止。

以降の小節で説明するように、これらの理由の1つ1つはVACUUM操作の実行について、その頻度の変動や対象領域の変動に影響します。

VACUUMには、標準VACUUMVACUUM FULLという2つの種類があります。 VACUUM FULLはより多くのディスク容量を回収することができますが、実行にとても時間がかかります。 また、VACUUMの標準形式は実運用のデータベースに対する操作と同時に実行させることができます。 (SELECTINSERTUPDATEDELETEなどのコマンドは通常通りに動作し続けます。 しかし、バキューム処理中はALTER TABLEなどのコマンドを使用してテーブル定義を変更することはできません。) VACUUM FULLはそれが作用するテーブルに対しACCESS EXCLUSIVEロックを必要とするので、それらテーブルのその他の用途と並行して行うことはできません。 一般的に、管理者は標準VACUUMの使用に努め、VACUUM FULLの使用を避けるべきです。

VACUUMは、かなりの量のI/Oトラフィックを発生させます。 このため、他の実行中のセッションの性能を劣化させる可能性があります。 バックグラウンドで実行されるバキューム処理による性能への影響を軽減させることを調整できるような設定パラメータがあります。 20.4.4を参照してください。

25.1.2. ディスク容量の復旧 #

PostgreSQLでは、行のUPDATEもしくはDELETEは古い行を即座に削除しません。 この方法は、多版同時性制御(MVCC第13章を参照してください)の恩恵を受けるために必要なものです。 あるバージョンの行は他のトランザクションから参照される可能性がある場合は削除されてはなりません。 しかし最終的には、更新された前の行や削除された行を参照するトランザクションはなくなります。 必要なディスク容量が無制限に増加しないように、これらが占める領域は、新しい行で再利用できるように回収されなければなりません。 これはVACUUMを実行することで行われます。

標準形式のVACUUMは、テーブルとインデックス内の無効な行バージョンを削除し、その領域を将来の再利用が可能であるものとして記録します。 しかし、その領域をオペレーティングシステムに返却することはありません。 例外として、テーブルの末尾に完全に空のページが存在し、かつそのテーブルの排他ロックが容易に獲得できるような特殊な場合には、その領域を返却します。 対照的にVACUUM FULLは、無効な領域のない全く新しいバージョンのテーブルファイルを書き出すことで、積極的にテーブルを圧縮します。 テーブルの容量を最小化しますが、長い時間がかかる可能性があります。 また操作が終わるまで、テーブルの新しいコピー用に余計なディスク領域を必要とします。

定常的なバキューム作業の通例の目安はVACUUM FULLの必要性を避けるに充分な頻度で標準VACUUMを行うことです。 自動バキュームデーモンはこのようにして作動を試みます。 そして実際VACUUM FULLを行いません。 この手法において、その発想はテーブルを最小サイズに保つのではなく、ディスク領域使用の安定状態を保持することです。 それぞれのテーブルは、その最小サイズにバキューム作業とバキューム作業の間で使用されることになる容量を加えたのに等しい空間を占有します。 VACUUM FULLは、テーブルをその最小サイズまで縮小し、ディスク空間をオペレーティングシステムに返却するために使用することができますが、もし将来そのテーブルが再び肥大化するのであれば、大した意味がありません。 従って、程よい頻度の標準VACUUMを実行するほうが、不定期のVACUUM FULLを実行するより大量の更新テーブルを保守するにはより良い取り組みとなります。

例えば負荷が少ない夜間に全ての作業を行うように、一部の管理者は自身で計画したバキューム作業の方を選びます。 固定したスケジュールに従ってバキューム作業を行うことについての問題は、もし更新作業によりテーブルが予期せぬ急増に遭遇した場合、空き領域を回収するためにVACUUM FULLが本当に必要となるところまで肥大化することです。 自動バキュームデーモンを使用することにより、この問題は緩和されます。 なぜなら、このデーモンは更新作業に反応して動的にバキューム作業を計画するからです。 完全に作業量を予測することができない限り、デーモンを完全に無効化するのは勧められません。 取り得る妥協案の1つは、いつになく激しい更新作業にのみ反応するよう、デーモンのパラメータを設定することです。 これにより、抑制可能な範囲を維持しつつ、負荷が標準的な場合に計画化されたVACUUMがまとめて作業を行うことを想定することができます。

自動バキュームを使用しない場合の典型的な方式は、データベース全体のVACUUMを1日1回使用頻度が低い時間帯にスケジュールすることです。 必要に応じて、更新頻度の激しいテーブルのバキューム処理をより頻繁に行うよう追加してください。 (非常に高い頻度でデータの更新を行うインストレーションの中では、分間隔位という頻度で高負荷なテーブルのVACUUMを行うこともあります。) 1つのクラスタで複数のデータベースがある場合、それぞれをバキュームすることを忘れないでください。 vacuumdbプログラムが役に立つかもしれません。

ヒント

大規模な更新や削除作業の結果としてテーブルが無効な行バージョンを大量に含む場合、通常のVACUUMは満足のゆくものではないかもしれません。 もしそのようなテーブルを所有し、それが占有する余分なディスク空間の回収が必要であれば、VACUUM FULL、またはその代わりにCLUSTERやテーブルを書き換えるALTER TABLE構文の1つを使用しなければなりません。 これらのコマンドはテーブル全体を新しいコピーに書き換え、それに対する新規インデックスを作成します。 これらの選択肢はすべてACCESS EXCLUSIVEロックを必要とします。 新しいものが完成するまで、テーブルの旧コピーとインデックスは解放されませんので、元のテーブルと同程度の容量の余計なディスク領域も一時的に使用することに注意してください。

ヒント

テーブルの内容が定期的に完全に削除される場合、DELETEの後にVACUUMを使用するよりも、TRUNCATEを使用する方が良いでしょう。 TRUNCATEはテーブルの全ての内容を即座に削除します。 また、その後に不要となったディスク容量を回収するためにVACUUMVACUUM FULLを行う必要がありません。 不利な点は厳格なMVCCの意味論に違反することです。

25.1.3. プランナ用の統計情報の更新 #

PostgreSQL問い合わせプランナは、優れた問い合わせ計画を作成するのに、テーブルの内容に関する統計情報に依存しています。 この統計情報はANALYZEコマンドによって収集されます。 このコマンドはそのものを呼び出す以外にも、VACUUMのオプション処理としても呼び出すことができます。 合理的な精度の統計情報を持つことは重要です。 さもないと非効率的な計画を選択してしまい、データベースの性能を悪化させてしまいます。

自動バキュームデーモンが有効になっている場合は、テーブルの内容が大きく変更されたときはいつでも自動的にANALYZEコマンドを発行します。 しかし、特にテーブルの更新作業が興味のある列の統計情報に影響を与えないことが判っている時、手作業により計画されたANALYZE操作を当てにする方が好ましいと管理者は思うかもしれません。 デーモンは、挿入または更新された行数の関数としてANALYZEを厳密に計画します。 しかし、意味のある統計情報の変更につながるかどうかは判りません。

パーティションや継承の子で変更されたタプルは親テーブルでの解析を誘発しません。 親テーブルが空であったり、まれにしか変更されなかったりする場合、自動バキュームにより処理されることはなく、継承ツリー全体としての統計情報は収集されないかもしれません。 統計情報を最新に保つためには、親テーブルでANALYZEを手動で実行することが必要です。

領域復旧のためのバキューム処理と同様、頻繁な統計情報の更新は、滅多に更新されないテーブルよりも更新の激しいテーブルにとってより有益です。 しかし、頻繁に更新されるテーブルであっても、データの統計的な分布が大きく変更されなければ、統計情報を更新する必要はありません。 単純な鉄則は、テーブル内の列の最小値、最大値にどのくらいの変化があったかを考えることです。 例えば、行の更新時刻を保持するtimestamp列の場合、最大値は行が追加、更新されるにつれて、単純に増加します。 こういった列は、おそらく、例えば、あるWebサイト上のアクセスされたページのURLを保持する列よりも頻繁に統計情報を更新する必要があるでしょう。 このURL列の更新頻度も高いものかもしれませんが、その値の統計的な分布の変更は相対的に見ておそらく低いものです。

特定のテーブルに対してANALYZEを実行することができます。 また、テーブルの特定の列のみに対してさえも実行することができます。 ですので、アプリケーションの要求に応じて、他よりも頻繁に一部の統計情報を更新できるような柔軟性があります。 しかし、実際には、操作が高速であるため、単にデータベース全体を解析することが最善です。 ANALYZEは、すべての行を読むのではなく、テーブルから統計的にランダムな行を抽出して使用します。

ヒント

列単位でのANALYZE実行頻度の調整はあまり実用的とは言えるものではありませんが、ANALYZEで集計される統計情報の詳細レベルの調整を列単位で行うことは価値がある場合があります。 WHERE句でよく使用され、データ分布の規則性がほとんどない列は、他の列よりもより細かいデータのヒストグラムが必要になるでしょう。 ALTER TABLE SET STATISTICSを参照するか、default_statistics_target設定パラメータでデータベース全体のデフォルトを変更してください。

またデフォルトで、関数の選択性に関して利用可能な制限付きの情報があります。 しかし、統計情報オブジェクトや関数呼び出しを使用する式インデックスを作成する場合、有用な統計情報が関数に関して収集されます。 これにより式インデックスを使用する問い合わせ計画を大きく改良することができます。

ヒント

自動バキュームデーモンは、有益になる頻度を決定する手段がありませんので、外部テーブルに対してANALYZEコマンドを発行しません。 問い合わせが適切な計画作成のために外部テーブルの統計情報が必要であれば、適当なスケジュールでこれらのテーブルに対して手作業で管理するANALYZEコマンドを実行することを勧めます。

ヒント

自動バキュームデーモンはパーティション化テーブルに対してANALYZEコマンドを発行しません。 継承の親は親自身が変更された場合にのみ解析されます。子テーブルの変更は親テーブルでの自動解析を誘発しません。 もし問い合わせが適切な計画のために親テーブルの統計情報を必要とするなら、統計情報を最新に保つために、それらのテーブルに対して定期的に手動でANALYZEを実行することが必要です。

25.1.4. 可視性マップの更新 #

バキュームは、どのページにすべての有効トランザクション(およびページが再度更新されるまでの将来のトランザクション)で可視であることが分かっているタプルのみが含まれるかを追跡するために、各テーブルの可視性マップの保守を行います。 2つの目的があります。 1つ目はバキューム自身が、整理するものがありませんので、こうしたページを次回飛ばすことができます。

2つ目は、PostgreSQLが、背後にあるテーブルを参照することなく、インデックスのみを使用して一部の問い合わせに応えることができるようになります。 PostgreSQLのインデックスにはタプルの可視性に関する情報を持ちませんので、通常のインデックススキャンは合致したインデックス項目のヒープタプルを取り込み、現在のトランザクションから可視であるべきかどうか検査します。 一方でインデックスオンリースキャンはまず可視性マップを検査します。 そのページのタプルがすべて可視であることが分かれば、ヒープの取り出しを省くことができます。 可視性マップによりディスクアクセスを防ぐことができる大規模なデータ群に対して、特に有効です。 可視性マップはヒープより非常に小さいため、ヒープが非常に大きい場合であっても簡単にキャッシュすることができます。

25.1.5. トランザクションIDの周回エラーの防止 #

PostgreSQLMVCCトランザクションのセマンティクスは、トランザクションID(XID)番号の比較が可能であることに依存しています。 現在のトランザクションのXIDよりも新しい挿入時のXIDを持ったバージョンの行は、未来のものであり、現在のトランザクションから可視であってはなりません。 しかし、トランザクションIDのサイズには制限(32ビット)があり、長時間(40億トランザクション)稼働しているクラスタはトランザクションの周回を経験します。 XIDのカウンタが一周して0に戻り、そして、突然に、過去になされたトランザクションが将来のものと見えるように、つまり、その出力が不可視になります。 端的に言うと、破滅的なデータの損失です。 (実際はデータは保持されていますが、それを入手することができなければ、慰めにならないでしょう。) これを防ぐためには、すべてのデータベースにあるすべてのテーブルを少なくとも20億トランザクションごとにバキュームする必要があります。

定期的なバキューム処理によりこの問題が解決する理由は、VACUUMが行に凍結状態という印をつけて、挿入トランザクションの効果が確実に可視になるような十分遠い過去にコミットされたトランザクションによりそれらが挿入されたことを表すからです。 PostgreSQLは特別なXID、FrozenTransactionIdを確保します。 このXIDは通常のXIDの比較規則には従わず、常に全ての通常のXIDよりも古いものとみなされます。 通常のXID(2以上の値)はmodulo-232という数式を使用して比較されます。 これは、全ての通常のXIDでは、20億のより古いXIDと20億のより新しいXIDが存在することを意味します。 言い換えると、通常のXID空間は終わることなく循環されているということです。 そのため、ある特定のXIDであるバージョンの行を作成すると、そのバージョンの行は、以降の20億トランザクションからはどの通常のXIDについて比較しているのかには関係なく、 過去のものと認識されます。 そのバージョンの行が20億トランザクション以上後にも存在していた場合、それは突然に未来のものとして認識されます。 これを防ぐために、凍結された行バージョンは挿入XIDがFrozenTransactionIdであるかのように扱われ、それで、周回問題に関係なく、すべての通常のトランザクションから過去のものとして認識され、また、そのバージョンの行はどれだけ古いものであろうと、削除されるまで有効状態となります。

注記

9.4より前のバージョンのPostgreSQLでは、行の挿入XIDを実際にFrozenTransactionIdで置換することで凍結が実装されており、これは行のxminシステム列として見えていました。 それより新しいバージョンでは単にフラグのビットをセットするだけで、行の元のxminは後の検証での利用に備えて保存します。 しかし、9.4以前のバージョンからpg_upgradeでアップグレードしたデータベースでは、xminFrozenTransactionId (2)に等しい行がまだあるかもしれません。

また、システムカタログにはxminBootstrapTransactionId (1)に等しい行が含まれる場合があり、これはその行がinitdbの最初の段階で挿入されたことを意味します。 FrozenTransactionIdと同様、この特別なXIDはすべての通常のXIDよりも古いものとして扱われます。

vacuum_freeze_min_ageは、その行バージョンが凍結される前に、XID値がどのくらい経過しているのかを制御します。 この設定値を大きくすることで、そうでなければ凍結状態になる行がすぐに再び修正されるのであれば、不必要な作業を避けられるかもしれませんが、この設定値を小さくすることでテーブルを次にバキュームする必要が起こるまで継続できるトランザクション数が増加します。

VACUUM可視性マップを使用して、テーブルのどのページを走査する必要があるかを決定します。 通常は、無効な行バージョンを持っていないページをスキップします。このとき、そのページに古いXID値の行バージョンがまだある可能性があったとしても読み飛ばします。 したがって、通常のVACUUMでは必ずしもテーブル内のすべての古い行バージョンを凍結するわけではありません。 そのようなことが起きた場合には、最終的にVACUUM積極的なバキュームを実行する必要があるでしょう。そのときは、全可視ではあるが全凍結ではないページにあるものを含めて、適切な凍結されていないXID値やMXID値をすべて凍結します。 実際には、ほとんどのテーブルには定期的な積極的なバキュームが必要です。 vacuum_freeze_table_ageVACUUMがいつこれを行うかを制御します。 つまり、最後にそのような走査が行われた後に実行されたトランザクションの数がvacuum_freeze_table_ageからvacuum_freeze_min_ageを引いた数より大きいとき、全可視ではあるが全凍結ではないページも走査されます。 vacuum_freeze_table_ageを0に設定するとVACUUMは常にこの積極的な戦略を使うようになります。

テーブルをバキュームすることなく処理できる最大の時間は、20億トランザクションから最後に積極的なバキュームを実行した時点のvacuum_freeze_min_ageの値を差し引いたものです。 この時間よりも長期間バキュームを行わないと、データ損失が発生するかもしれません。 これを確実に防止するために、自動バキュームがautovacuum_freeze_max_age設定パラメータで指定された時代より古いXIDを持つ、凍結状態でない行を含む可能性がある任意のテーブルに対して呼び出されます。 (これは自動バキュームが無効であっても起こります。)

これは、あるテーブルがバキュームされていなかったとしても、自動バキュームがおよそautovacuum_freeze_max_age - vacuum_freeze_min_ageトランザクション毎に呼び出されることを意味します。 領域確保のために定常的にバキューム処理を行うテーブルでは、これは重要ではありません。 しかし、(挿入のみで更新や削除が行われないテーブルを含む)静的なテーブルでは、領域確保のためのバキューム処理を行う必要がなくなりますので、非常に長期間静的なテーブルでは、強制的な自動バキューム間の間隔を最大まで延ばすことができます。 記載するまでもありませんが、autovacuum_freeze_max_ageを増やすことでもvacuum_freeze_min_ageを減らすことでも、これを行うことができます。

vacuum_freeze_table_ageに対する有効な最大値は0.95 * autovacuum_freeze_max_ageです。 これより値が高いと値は最大値までに制限されます。 autovacuum_freeze_max_ageより高い値は、周回防止用の自動バキュームがその時点でいずれにせよ誘発され、0.95という乗算係数がそれが起こる前に手動によるVACUUM実行の余地を残すため、意味を持ちません。 経験則に従うと、定期的に計画されたVACUUMもしくは通常の削除・更新作業により誘発された自動バキュームがその期間で実行されるように十分な間隔を残しておくように、vacuum_freeze_table_ageautovacuum_freeze_max_ageより多少低い値に設定されるべきです。 これを余りにも近い値に設定すると、たとえ領域を回収するために最近テーブルがバキュームされたとしても、周回防止用の自動バキュームに帰着します。 一方より低い値はより頻繁な積極的バキュームを引き起こします。

autovacuum_freeze_max_age(およびそれに付随するvacuum_freeze_table_age)を増やす唯一の欠点は、データベースクラスタのサブディレクトリpg_xactpg_commit_tsがより大きな容量となることです。 autovacuum_freeze_max_ageの範囲まですべてのトランザクションのコミット状況と(track_commit_timestampが指定されていれば)タイムスタンプを格納しなければならないためです。 コミット状況は1トランザクション当たり2ビット使用しますので、もしautovacuum_freeze_max_ageをその最大許容値である20億に設定している場合、pg_xactはおよそ0.5ギガバイトまで、pg_commit_tsは約20GBまで膨らむものと考えられます。 これがデータベースサイズ全体に対してとるに足らないものであれば、autovacuum_freeze_max_ageを最大許容値に設定することを勧めます。 さもなければ、pg_xactpg_commit_tsの容量として許容できる値に応じてそれらを設定してください。 (デフォルトは2億トランザクションです。換算するとpg_xactはおよそ50MB、pg_commit_tsはおよそ2GBの容量となります。)

vacuum_freeze_min_age を減らすことにも1つ欠点があります。 これによりVACUUMが大して役に立たなくなるかもしれません。 テーブル行がすぐに変更される場合(新しいXIDを獲得することになります)、行バージョンを凍結することは時間の無駄です。 そのため、この設定は、行の変更が起こらなくなるまで凍結されない程度に大きくすべきです。

データベース内のもっとも古い凍結されていないXIDの年代を追跡するために、VACUUMはシステムテーブルpg_classpg_databaseにXID統計情報を保持します。 特に、テーブルに対応するpg_class行のrelfrozenxid列には、relfrozenxidを進めることに成功した最後のVACUUM(典型的には最後の積極的なVACUUM)の終わりに残っているもっとも古い凍結されていないXIDが含まれます。 同様に、データベースに対応するpg_database行のdatfrozenxid列は、データベース内で現れる凍結されていないXIDの下限値です。 これは、そのデータベース内のテーブル当たりのrelfrozenxid値の最小値です。 この情報を検査する簡便な方法は、以下の問い合わせを実行することです。

SELECT c.oid::regclass as table_name,
       greatest(age(c.relfrozenxid),age(t.relfrozenxid)) as age
FROM pg_class c
LEFT JOIN pg_class t ON c.reltoastrelid = t.oid
WHERE c.relkind IN ('r', 'm');

SELECT datname, age(datfrozenxid) FROM pg_database;

age列は切り捨てXIDから現在のトランザクションXIDまでのトランザクション数を測ります。

ヒント

VACUUMコマンドのVERBOSEパラメータが指定されている場合、VACUUMはテーブルに関するさまざまな統計情報を出力します。 これにはrelfrozenxidおよびrelminmxidをどのように繰り上げたかの情報や新しく凍結されたページの数が含まれます。 (log_autovacuum_min_durationで制御される)自動バキュームロギングが自動バキュームによって実行されたVACUUM操作を報告する場合にも、サーバログに同じ詳細が表示されます。

VACUUMは通常は最後のバキュームの後で変更されたページのみ走査しますが、relfrozenxidはテーブルの凍結されていないXIDを含むかもしれないすべてのページを走査したときのみ繰り上がります。 これは、relfrozenxidvacuum_freeze_table_ageトランザクション年齢より大きい時、VACUUMFREEZEオプションが使用された時、もしくは無効な行バージョンを削除するため全凍結になっていないすべてのページをバキュームしなければならなくなった時に発生します。 VACUUMがテーブルの全凍結になっていないすべてのページを走査したとき、age(relfrozenxid)は、使用されたvacuum_freeze_min_age設定より若干大きくなるはずです (VACUUMを起動してから始まったトランザクションの数分大きくなります)。 VACUUMrelfrozenxidをテーブルに残っている最も古いXIDに設定しますので、最後の値を厳密に要求されるものよりずっとより新しいものとすることが可能です。 relfrozenxidを繰り上げるVACUUMautovacuum_freeze_max_ageに達するまでにテーブルに対して発行されない場合、そのテーブルに対して自動バキュームが早急に強制されます。

何らかの理由により自動バキュームがテーブルの古いXIDの整理に失敗した場合、システムはデータベースの最古のXIDが周回ポイントから4000万トランザクションに達した場合と似たような警告メッセージを発行し始めます。

WARNING:  database "mydb" must be vacuumed within 39985967 transactions
HINT:  To avoid a database shutdown, execute a database-wide VACUUM in that database.

(ヒントで示唆されたように手動VACUUMはこの問題を解決します。 しかし、VACUUMはスーパーユーザで実行されるべきであること注意してください。 さもないとシステムカタログの処理に失敗し、このためデータベースのdatfrozenxidを繰り上げることができません。) こうした警告も無視し続け、周回するまでのトランザクションが300万より少なくなると、システムは新しいXIDの割り当てを拒絶します。

ERROR:  database is not accepting commands to avoid wraparound data loss in database "mydb"
HINT:  Stop the postmaster and vacuum that database in single-user mode.

この状態では、すでに進行中のトランザクションは継続できますが、読み込み専用トランザクションのみを開始できます。 データベースレコードを変更したり、リレーションを切り詰めたりする操作は失敗します。 VACUUMコマンドは通常どおりに実行できます。 ヒントが述べていることに反して、通常の操作を復元するために、postmasterを停止したりシングルユーザモードに入ったりする必要はなく、また、それが望ましいことでもありません。 代わりに、次の手順を実行してください。

  1. 古いプリペアドトランザクションを解決します。 pg_prepared_xactsage(transactionid)が大きい行を確認して見つけることができます。 このようなトランザクションはコミットまたはロールバックされるべきです。
  2. 長時間実行されているオープントランザクションを終了します。 pg_stat_activityage(backend_xid)またはage(backend_xmin)が大きい行を確認して、これらを見つけることができます。 このようなトランザクションはコミットまたはロールバックするか、pg_terminate_backendを使用してセッションを終了できます。
  3. 古いレプリケーションスロットを削除します。 pg_stat_replicationを使用してage(xmin)またはage(catalog_xmin)が大きいスロットを見つけます。 多くの場合、そのようなスロットは、もはや存在しないか長い間ダウンしているサーバへのレプリケーションのために作成されたものです。 存在するサーバに対してスロットを削除しても、そのスロットに接続しようとする可能性がある場合、そのレプリカは再構築する必要があるでしょう。
  4. 対象のデータベースでVACUUMを実行します。 データベース全体のVACUUMが最も簡単です。 必要な時間を短縮するために、relminxidが最も古いテーブルに対して手動VACUUMコマンドを発行することも可能です。 このシナリオではVACUUM FULLを使用しないでください。これにはXIDが必要であり、スーパーユーザモード以外では失敗します。 その代わりにXIDを消費してトランザクションIDのラップアラウンドのリスクを高めるからです。 また、VACUUM FREEZEも使用しないでください。 通常の操作を回復するために必要な最小限の作業以上の作業を行うからです。
  5. 通常の動作が回復したら、将来の問題を回避するために、対象のデータベースで自動バキュームが正しく設定されていることを確認してください。

注記

以前のバージョンでは、postmasterを停止してVACUUMをシングルユーザモードで実行する必要な場合がありました。 一般的なシナリオでは、これはもはや必要ではなく、システムを停止させることを伴うため、可能な限り回避する必要があります。 また、データ損失を防ぐために設計されたトランザクションIDラップアラウンド保護を無効にするため、よりリスクが高くなります。 このシナリオでシングルユーザモードを使用する唯一の理由は、不必要なテーブルをTRUNCATEまたはDROPし、それらのVACUUMをする必要を避けたい場合です。 管理者がこのようなことを行えるようにするために、300万トランザクションの安全マージンが存在します。 シングルユーザモードの使用の詳細については、postgresのリファレンスページを参照してください。

25.1.5.1. マルチトランザクションと周回 #

マルチトランザクションIDは複数のトランザクションによる行ロックをサポートするのに使われます。 タプルヘッダにはロック情報を格納するために限られた容量しかありませんので、二つ以上のトランザクションが同時に行をロックする時には必ず、その情報はマルチプル(訳注:複数の)トランザクションID、略してマルチトランザクションID、にエンコードされます。 あるマルチトランザクションIDにどのトランザクションIDが含まれているかという情報はpg_multixactサブディレクトリに別に格納されており、マルチトランザクションIDのみがタプルヘッダのxmaxフィールドに現れます。 トランザクションIDと同様に、マルチトランザクションIDは32ビットカウンタと対応する記憶領域として実装されており、どちらも注意深い年代管理や記憶領域の整理、周回の取り扱いが必要です。 各マルチトランザクションにはメンバの一覧を保持する独立した記憶領域があり、そこでも32ビットカウンタを使っているので同じように管理しなければなりません。

テーブルの何らかの部分に対しVACUUM走査されるときはいつでも、そのときに見つかったvacuum_multixact_freeze_min_ageよりも古いマルチトランザクションIDはすべて異なる値で置き換えられます。 異なる値とは、0かもしれませんし、単一のトランザクションIDかもしれませんし、より新しいマルチトランザクションIDかもしれません。 各テーブルでは、pg_class.relminmxidがそのテーブルのタプルにまだ現れるマルチトランザクションIDのうちできるだけ古いものを保持しています。 この値がvacuum_multixact_freeze_table_ageよりも古ければ、積極的バキュームが強制されます。 前節で説明したように、積極的なバキュームでは全凍結であるとわかっているページのみがスキップされます。 pg_class.relminmxidに対してその年代を調べるのにmxid_age()を使えます。

積極的なVACUUMは、その原因が何かに関わらず、そのテーブルのrelminmxidを繰り上げできることが保証されています。 結局、データベースすべてのテーブルすべてが走査され、最も古いマルチトランザクション値が繰り上げられますので、ディスク上でより古いマルチトランザクションを保持している領域は削除できます。

安全装置として、autovacuum_multixact_freeze_max_ageよりもそのマルチトランザクション年代が大きいどのテーブルに対しても、積極的なバキューム走査が起こります。 また、マルチトランザクションメンバによるストレージの占有が2GBを超えた場合にも、積極的なバキューム走査は、マルチトランザクション年代の一番古いものから始めて、すべてのテーブルに対してより頻繁に起こります。 この種の積極的走査はどちらも、自動バキュームが名目上は無効にされていても発生します。

XIDの場合と同様に、自動バキュームがテーブルから古いMXIDをクリアできない場合、データベースの最も古いMXIDが周回ポイントから4000万トランザクションに達すると、システムは警告メッセージの出力を開始します。 そして、XIDの場合と同様に、こうした警告を無視し続け、周回するまで3000万を切ると、システムは新しいMXIDの生成を拒絶します。

MXIDが枯渇したときの通常の動作は、XIDが枯渇したときとほぼ同じ方法で復元できます。 前のセクションと同じ手順に従いますが、次の点が異なります。

  1. 実行中のトランザクションとプリペアドトランザクションは、マルチトランザクションに現れる可能性がない場合は無視できます。
  2. pg_stat_activityなどのシステムビューではMXID情報は直接表示されませんが、古いXIDを探すことはMXIDの周回問題の原因となっているトランザクションを判断する良い方法です。
  3. XIDの枯渇はすべての書き込みトランザクションをブロックしますが、MXIDの枯渇は、特に MXIDを必要とする行ロックを含む書き込みトランザクションのサブセットのみをブロックします。

25.1.6. 自動バキュームデーモン #

PostgreSQLには、省略可能ですが強く推奨される自動バキュームという機能があります。 これはVACUUMANALYZEコマンドの実行を自動化することを目的としたものです。 有効にすると、自動バキュームは大量のタプルの挿入、更新、削除があったテーブルを検査します。 この検査は統計情報収集機能を使用します。 したがって、track_countstrueに設定されていないと、自動バキュームを使用することができません。 デフォルトの設定では、自動バキュームは有効で、関連するパラメータも適切に設定されています。

実際のところ自動バキュームデーモンは複数のプロセスから構成されます。 自動バキュームランチャという永続的デーモンプロセスが存在し、自動バキュームワーカープロセスがすべてのデータベースを処理します。 ランチャは、1つのワーカーを各データベースに対しautovacuum_naptime秒ごとに開始するよう試みることにより、時間に対して作業を分散化します。 (したがってインストレーションにN個のデータベースがある場合、新規ワーカーがautovacuum_naptime/N秒毎に起動されます。) 同時に最大autovacuum_max_workers個のプロセスが実行可能です。 処理対象のデータベースがautovacuum_max_workersより多くある場合、次のデータベースは最初のワーカーが終了するとすぐに処理されます。 それぞれのワーカープロセスはデータベース内の各テーブルを検査し、必要に応じてVACUUMまたはANALYZEコマンドを発行します。 log_autovacuum_min_durationも自動バキュームワーカーの活動を監視するために設定できます。

短期間にいくつかの大規模なテーブルがすべてバキューム対象として適切な状態になったとすると、すべての自動バキュームワーカーはこうしたテーブルに対するバキューム処理に長い期間占領される可能性があります。 これにより、ワーカーが利用できるようになるまで、他のテーブルやデータベースに対するバキュームが行われなくなります。 また、単一データベースに対するワーカー数には制限はありませんが、ワーカーはすでに他のワーカーによって実行された作業を繰り返さないように試みます。 ワーカーの実行数はmax_connections制限にもsuperuser_reserved_connections制限にも計上されないことに注意してください。

テーブルのrelfrozenxid値がautovacuum_freeze_max_ageトランザクション年齢よりも古い場合、そのテーブルは常にバキュームされます (これはfreeze max ageがストレージパラメータにより変更されたテーブルに対しても適用されます。以下を参照)。 さもなければ、直前のVACUUMの後に不要となったタプル数がバキューム閾値を超えると、テーブルはバキュームされます。 このバキューム閾値は以下のように定義されます。


バキューム閾値 = バキューム基礎閾値 + バキューム規模係数 * タプル数

ここで、バキューム基礎閾値はautovacuum_vacuum_threshold、バキューム規模係数はautovacuum_vacuum_scale_factor、タプル数はpg_class.reltuplesです。

直前のバキュームの後に挿入されたタプル数が定義された挿入閾値を超えた場合も、テーブルはバキュームされます。ここで挿入閾値は以下のように定義されます。


バキューム挿入閾値 = バキューム基礎挿入閾値 + バキューム挿入規模係数 * タプル数

ここで、バキューム挿入基礎閾値はautovacuum_vacuum_insert_threshold、バキューム挿入規模係数はautovacuum_vacuum_insert_scale_factorです。 そのようなバキュームは、テーブルの一部を全可視と印づけたり、タプルを凍結したりもできますので、後続のバキュームで必要となる作業を減らせます。 より早いバキュームによりタプルを凍結できますので、INSERT操作を受けたもののUPDATE/DELETE操作を全くもしくはほとんど受けていないテーブルに対しては、テーブルのautovacuum_freeze_min_ageを低くすることが有益な場合があります。 不要となったタプル数と挿入されたタプル数は、累積統計情報システムから取得されます。 これは、UPDATEDELETEおよびINSERT操作ごとに更新されるほぼ正確な数です。 (負荷が高いと一部の情報が失われる可能性があることから、これはほぼ正確な数でしかありません。) テーブルのrelfrozenxid値がvacuum_freeze_table_ageトランザクション年齢より大きい場合、古いタプルを凍結して、relfrozenxidを繰り上げるため、積極的なバキュームが実行されます。 そうでなければ最後のバキュームの後に変更されたページのみ走査されます。

解析でも似たような条件が使用されます。 以下で定義される閾値が、


解析閾値 = 解析基礎閾値 + 解析規模係数 * タプル数

前回のANALYZEの後に挿入、更新、削除されたタプル数と比較されます。

パーティション化テーブルはタプルを直接格納しないため、自動バキュームによって処理されません。 (自動バキュームは他のテーブルと同様にテーブルパーティションを処理します。) 残念ながら、これは自動バキュームがパーティション化テーブルでANALYZEを実行しないことを意味し、これによりパーティション化テーブルの統計を参照する問い合わせに対して最適でない計画を生成する可能性があります。 この問題を回避するには、パーティション化テーブルに最初にデータが移入されたときにANALYZEを手動で実行し、パーティション内のデータの分布が大きく変化した場合には必ず再度実行します。

一時テーブルには自動バキュームでアクセスすることはできません。 したがってセッションのSQLコマンドを用いて適切なバキュームおよび解析操作を行わなければなりません。

デフォルトの閾値と規模係数は、postgresql.confから取られますが、(他の多くの自動バキューム制御パラメータと合わせて)テーブル毎に上書きすることができます。 より詳細な情報は格納パラメータを参照してください。 テーブルのストレージパラメータで設定が変更されると、そのテーブルを処理する時にその値が使用されます。 そうでなければ、全体設定が使われます。 全体設定についての詳細な情報は20.10を参照してください。

複数のワーカープロセスが実行している場合、自動バキュームコスト遅延パラメータ(20.4.4を参照してください)は実行中のワーカー全体に振り分けられます。 このため、ワーカーの実稼働数に関らず、システムに与えるI/Oの総影響は変わりありません。 しかし、テーブル毎のautovacuum_vacuum_cost_delayまたはautovacuum_vacuum_cost_limitストレージパラメータが設定されたテーブルを処理するワーカーは振り分けアルゴリズムでは考慮されません。

自動バキュームワーカーは通常は他のコマンドをブロックしません。 自動バキュームが保持するSHARE UPDATE EXCLUSIVEロックと衝突するロックを、プロセスが獲得しようとした場合には、ロックの獲得により自動バキュームが中断されます。 衝突するロックモードに関しては表 13.2を参照してください。 しかしながら、自動バキュームがトランザクションIDの周回を防ぐために動作している(すなわち、pg_stat_activityビューの自動バキューム問い合わせ名が(to prevent wraparound)で終わっている)場合には、自動バキュームは自動的には中断されません。

警告

SHARE UPDATE EXCLUSIVEロックと衝突するロックを獲得する、定期的に動作するコマンド(例えばANALYZE)により、自動バキュームが実質的に終わらなくなることがあります。