こんにちわ、田原です。
お客様対応でpostgresqlのバキューム処理について調査する機会があり、
まとめましたので、内容を紹介します。
なぜpostgresqlにバキューム処理が必要なのか
postgresqlには以下の点を解決するため、バキューム処理が実装されています。
PostgreSQLのMVCC(Multi-Version Concurrency Control)の影響
PostgreSQLは同時実行制御にMVCC(Multi-Version Concurrency Control)を使用しており、
これが「デッドタプル」を生成する原因となります。
- UPDATE操作: 既存の行を物理的に更新せず、新しいバージョンの行を作成し、古い行を「デッド」としてマーク

- DELETE操作: 行を物理的に削除せず、削除マークを付けるだけ
この場合、デッド、削除マークの領域はそのままpostgresql管理として残っていて、
OSに領域を返すということはありません。
また、Insertやupdateを実行しても、その領域を再利用されることはなく、
残り続けてしまいます。
上記の動作から以下の問題点があります。
パフォーマンスの劣化
- テーブルサイズの肥大化により、スキャン時間が増加
- インデックスも肥大化し、検索効率が低下
- ディスク容量の無駄遣い
システムリソースの圧迫
- 不要なディスクI/O増加
- メモリ使用量の増加
- バックアップ時間の延長
統計情報の最新化
データの変動があった場合、統計情報を最新化しないと適切な実行計画が立てられず、パフォーマンスが劣化する可能性が高くなります。
そのため、システムを解析して統計情報を最新化する必要があります。
バキューム処理の役割と種類
・データベースの不要領域
・統計情報の最新化
前述の課題を解決するため、バキューム処理が用意されており、実行する必要があります。
また、バキューム処理にも以下の種類がありますので、そちらを役割ごとに説明していきます。
バキューム処理 ---+--- 自動実行 --- 自動バキューム処理(AUTO VACUUM) | +--- 手動実行 --- VACUUM コマンド ---+--- 標準 VACUUM | +--- VACUUM FULL ※VACUUMコマンドのオプションの一つという扱い
データベースの不要領域の回収
バキューム処理は、デッドタプルとなっている領域を回収します。
※タプル :データベースでは「レコード」のこと。
※デッドタプル:レコードから削除や更新されて非表示となったレコードのこと。
自動バキューム処理(AUTO VACUUM)
領域を回収し、再利用可能な状態に変更します。(デッドタプルにする)。
排他的ロックを取得しないため、テーブルへの通常の読み書き操作と並行して実行することが可能です。
しかし、余った領域は OS には(ほとんどの場合)返されず、同じテーブル内で再利用できるように保持されるだけとなります。
索引に対しても同様の効果があります。
標準 VACUUM
動作は「自動バキューム処理」と同じです。
※一般的に管理者は標準 VACUUM を使用してください。。
VACUUM FULL
テーブルの内容全体を新しいディスクファイルに領域を余すことなく書き換えて、
OS に未使用の領域を返します。
合わせて索引の再構築も実施されます。
統計情報の取得
より良い実行計画を作成するのに、テーブルに関する統計情報が必要です。
ANALYZE コマンドで統計情報を取得することができますが、バキューム処理も同様に取得が可能となっています。
自動バキューム処理(AUTO VACUUM)
テーブルの内容が大きく変更されたときはいつでも自動的に ANALYZE コマンドを実施して、統計情報を取得します。
なお、パーティション化テーブルに対しては、ANALYZE コマンドを実施しません。
標準 VACUUM
VACUUM 単独では実施されません。
オプションの「ANALYZE」を付与する必要があります。
もちろん、このオプション付きで実施することで、「不要領域の回収」と「統計情報の取得」が行われます。
VACUUM FULL
意外と思いますが、VACUUM FULL だけでは実施されません。
オプションの「ANALYZE」と一緒に実行する必要があります。
【コマンド例】vacuum full analyze <テーブル名>;
- vacuum analyze full <テーブル名>; で実行するとエラーになります。
- vacuum full analyze <テーブル名>; で実行すると、内部では vacuum full → analyze の順番で実施されています。(実行時を監視した結果より判断しています。)
VACUUM FULL と REINDEX の違い
VACUUM FULLとREINDEXの双方とも索引の再構築が実施されます。
索引の再構築中の出力例
postgres=> select * from pg_stat_progress_cluster;
-[ RECORD 1 ]-------+-----------------
pid | 2119
datid | 5
datname | postgres
relid | 106524
command | VACUUM FULL
phase | rebuilding index ★
cluster_index_relid | 0
heap_tuples_scanned | 15436442
heap_tuples_written | 15436442
heap_blks_total | 237483
heap_blks_scanned | 237483
index_rebuild_count | 2
なお、VACUUM FULLとREINDEXコマンドでは目的や対象が異なります。
また、索引が破損した場合はVACUUM FULLでは対応できません。
■VACUUM FULL
目的:ディスクスペースの回収
対象:テーブルと索引
索引の破損:修復不可
■REINDEX
目的:索引の再作成
対象:索引
索引の破損:修復を試行
関連ビュー
pg_stat_all_tablesビュー
VACUUM と ANALYZE を実行したタイミングを確認できます。
pg_stat_progress_vacuumビュー
VACUUM の進捗状況を確認するビューです。
ただし、VACLUUM FULL に関しては pg_stat_progress_clusterビュー側で確認する必要があります。
pg_stat_progress_clusterビュー
VACUUM FULL の進捗状況を確認するビューです。
ビューの名称から主に CLUSTER コマンド用ですが、VACUUM FULL は CLUSTER の一種のため、
このビューで進捗状況を確認します。
pg_stat_progress_analyzビュー
ANALYZE の進捗状況を確認するビューです。
VACUUM ANALYZE や VACUUM FULL ANALYZE のうち、ANALZYE フェーズはこちら側で進捗を確認する必要があります。
最後に
vacuum処理は意外と奥が深く、postgresqlにはなくてはならないものだと感じました。
