Dev Study
← データベースの仕組み
レッスン 3 / 13·約 9 分·第6期 データベースの仕組み

掃除は4つの仕事をしている — そしてディスクは空かない

リクエストがアプリケーションに届くまで
  1. ブラウザURL入力
  2. DNS名前→IP
  3. ルータ経路選択
  4. ロードバランサ振り分け
  5. Nginx受け口
  6. アプリ処理
  7. DB保存今ここ

ストレージの段で見た「容量」と、ここで言う「空間が空く」は同じではありません。データベースが自分で抱えたまま再利用する層が間に入ります。なお WAL・インデックス・分離レベルはまだ扱っていません。

まず結論

`VACUUM` が必要な理由は、よく語られる2つ(不要行の回収・統計情報の更新)ではありません。PostgreSQL 18 の公式は "several reasons" として**4つ**を番号付きで列挙しています。残る2つを知らないと、なぜ何もしていない表まで掃除されるのかが説明できません。

そしてもう一点。**通常の `VACUUM` はOSにディスクを返しません。** 公式は "extra space is not returned to the operating system (in most cases)" と明記しています。空間は同じ表の中で**再利用可能になるだけ**です。「`VACUUM` すればディスクが空く」は、この形では成り立ちません。

? 読む前に予測

表の途中にある行を大量に `DELETE` したあと通常の `VACUUM` を実行しました。OS から見たディスクの空き容量はどうなるでしょうか。

当てることが目的ではありません。外した予測ほど記憶に残るので、直感で選んでください。

抽象原理

「several reasons」と公式は書いている

VACUUM の説明を求めると、たいてい2つの理由が返ってきます。不要になった行を片付けること、そして統計情報を新しくすること。どちらも正しいのですが、公式が挙げている数は2つではありません。

PostgreSQL 18 の routine-vacuuming は、こう始まります。

PostgreSQL's VACUUM command has to process each table on a regular basis for several reasons:

  1. To recover or reuse disk space occupied by updated or deleted rows.
  2. To update data statistics used by the PostgreSQL query planner.
  3. To update the visibility map, which speeds up index-only scans.
  4. To protect against loss of very old data due to transaction ID wraparound or multixact ID wraparound.

番号が振られて4つです。よく語られる2つは、この並びの前半にあたります。

後半の2つを知らないままだと、説明できない現象が出てきます。3番目は、走査を速くするための地図の更新です。4番目は性質がまったく違います。「非常に古いデータが失われるのを防ぐ」——つまり掃除は片付けであると同時に、データを守る仕事でもあるということです。

この4番目があるために、行を1件も更新していない表にも掃除が必要になります。片付けの理由だけで考えていると、この事実は不可解に見えます。番号の3と4は、5番目以降のレッスンで正面から扱う主題につながります。ここではまず「理由は4つある」という事実を持ち帰ってください。

✓ ここまでの確認

公式が `VACUUM` の理由として列挙しているのは何個で、よく語られる「不要行の回収」「統計情報の更新」以外に何が含まれますか。

具体実物

回収するが、返却はしない

ここが本題です。掃除をすればディスクが空く、と考えたくなります。公式 sql-vacuum の記述を順に読みます。

Plain VACUUM (without FULL) simply reclaims space and makes it available for re-use.

空間を回収し、再利用できるようにする。ここまでは期待どおりです。

This form of the command can operate in parallel with normal reading and writing of the table, as an exclusive lock is not obtained.

排他ロックを取らないので、表への通常の読み書きと並行して動きます。本番稼働中に走らせられる掃除です。

そして次の一文が、期待と現実を分けます。

However, extra space is not returned to the operating system (in most cases); it's just kept available for re-use within the same table.

OSには返りません。 同じ表の中で「使ってよい場所」として保持されるだけです。

公式が使っている動詞の違い表
reclaim 空間を回収する … 通常のVACUUMが行う
mark 再利用可能と印を付ける … 通常のVACUUMが行う
re-use 同じ表の中で使い回す … 以後のINSERTが使う
return OSに返す … 通常のVACUUMは行わない

4行目だけが起きません。動詞が違えば意味が違う——reclaim と return を同じ「空く」にまとめた瞬間に、監視グラフを見て慌てることになります。

なお VACUUM にはもう一つの形があり、そちらはOSへの返却を行います。ただし挙動が根本的に違うため、次回まとめて扱います。

✓ ここまでの確認

通常の `VACUUM` が不要行に対して行うことを、公式の記述に最も近い形で述べているのはどれですか。

⇄ 2つを見比べる
具体例 A

通常の VACUUM が行うこと

不要になった行の版を表とインデックスから除き、"marks the space available for future reuse" します。領域そのものは同じ表が抱えたままで、次の `INSERT` がそこに入ります。公式の言葉では "it's just kept available for re-use within the same table" です。

表の途中の1万行を DELETE → 通常のVACUUM表
段階 表のファイルサイズ OSの空き容量
DELETE 直後 変わらない 変わらない
VACUUM 後 変わらない 変わらない
次のINSERT 変わらない(再利用) 変わらない
具体例 B

ファイルを消したのに容量が戻らないとき

OSの層でも同じ形が起きます。使用中として開かれている間は、名前を消しても領域は解放されません。使う側が手を離した時点で初めて返ります。「使っている誰かがいる間は返せない」という条件が、層を変えて現れています。

$ df -h を削除の前後で見る表
段階 df の空き容量
削除前 変わらない
名前を消した直後 変わらない
使う側が手を離した後 増える

データベースの `VACUUM` と、OSでのファイル削除。層は違いますが、「消したのに空かない」が起きる理由に共通する構造は何でしょうか。

どちらも「消してよいか」の判定と「領域を返すか」の判断が、別のタイミングで行われています。

この章のまとめ

  • 公式が挙げる VACUUM の理由は "several reasons" として4つ。不要行の回収・統計情報の更新・visibility map の更新・周回による非常に古いデータの喪失の防止。通説の2つは前半だけ。
  • 古い版が残るのは MVCC の利益を得るために必要だから。"must not be deleted while it is still potentially visible to other transactions"。
  • 通常の VACUUM は "marks the space available for future reuse"。回収(reclaim)はするが返却(return)はしない。
  • "extra space is not returned to the operating system (in most cases)"。使用率が下がらないのは失敗ではない。見るべきは増え続けていないか。
  • 排他ロックを取らないため "can operate in parallel with normal reading and writing of the table"。
  • OSに空間を返すもう一つの形が存在する。挙動が根本的に違うため次回扱う。

自分の現場で確かめる

読んだだけでは身につきません。手元の環境で実際に見てみてください。

環境:

理解度チェック

読んだ直後は「分かった」と感じやすいものです。実際に解いて確かめてください。

問題 1 / 3正解: 0

「`UPDATE` すると古い行はその場で消える」。この説明を公式の記述に照らして評価してください。

用語集(4語)
VACUUM
公式が挙げる目的は4つ。"To recover or reuse disk space occupied by updated or deleted rows." "To update data statistics used by the PostgreSQL query planner." "To update the visibility map, which speeds up index-only scans." "To protect against loss of very old data due to _transaction ID wraparound_ or _multixact ID wraparound_."
dead row versions(不要になった行の版)
更新・削除された行の古い版のうち、"no longer of interest to any transaction" となったもの。通常の `VACUUM` はこれを表とインデックスから除き、"marks the space available for future reuse" する。
reclaim(回収)と return(返却)
公式は書き分けている。通常の `VACUUM` は "simply reclaims space and makes it available for re-use" だが、"extra space is not returned to the operating system (in most cases)"。回収しても返却はしない。
visibility map
`VACUUM` の理由の3番目に挙げられる更新対象。公式の記述は "To update the visibility map, which speeds up index-only scans."。索引だけで済ませる走査を速くする役割を持つ。その仕組みは本レッスンでは扱わない。