HOTの仕組み(2)

HOTの仕組み(2)

どうしてガベージの発生量が減ったのか?

これの答えは、「常時行われる処理の中で、ガベージの回収を行うようになったから」です。PostgreSQLは、更新処理や削除処理を行った場合、古いデータは消さずに残しておきます。この古いデータ = ガベージを除去し、空き領域として再利用可能とする処理がVACUUMです。HOTは、常時実施されている SELECT, INSERT, UPDATE の処理の中で、VACUUM相当の処理をページ単位で実施するようになったのです(図5)。

図5. HOTによるガベージの掃除

しかし、これだけ見ると単純にVACUUMを細かな単位でバックグラウンド的に実施されるようになっただけに見えますね。実は、これが実現できたのは先ほどの「インデックスの不要な更新をスキップ」できるようになったからなのです。ガベージの掃除をする時に最も注意するのは、除去する行が完全に他から参照されていないことのチェックです。特に、インデックスから指されている行を除去するときは、その行を指すインデックス・キーが存在しないことを調べる必要があります。しかし、ラインポインタのリダイレクトを活用することで、インデックスやラインポインタ経由でガベージを指すルートを無くすことができました。図4の下段のガベージの状態がそれに当たります。このため、現実的なコストで、常時のガベージ回収が可能となったわけです。

ちなみに、ページ内のデータの移動にもコストがかかるので、ガベージの掃除は必要になった時のみ実施されます。「必要になった時」とは、あるページへのスキャンが発生した際、以下の条件にマッチした時となります。

  • ページ内の空き領域が、FILLFACTORの設定値か、ブロックサイズ(デフォルトは8192byte)の1/10以下になった場合
  • 直近の更新処理がページ内に空き領域を見つけられなかった場合
      • この際は、「このページはもう空き領域がない」というフラグを当該のページに立てて、以降の処理時のための「このページのガベージ回収が必要」というヒントにします

さて、ここで「FILLFACTOR」という言葉が出てきました。これは、予め確保するページ内の空き領域を設定するパラメータです。これはHOTを活用する上で重要なパラメータになります。次回の「HOTの上手な使い方」の中で詳しく説明します。

VACUUMの掃除はもう必要ない?

HOTにより、ガベージの自動回収がされることが分かりました。では、もうガベージの掃除を目的としたVACUUMは必要ないのでしょうか?答えは、「従来の頻度より減らせますが、まだ必要となる」ことになります。HOTの掃除では、完全に全てのガベージを除去できません。具体的には、ラインポインタがHOT更新されたレコード毎に一つ余分に残ります(図6)。

図6. HOTによるガベージの掃除時のラインポインタの扱い

図の通り、チェインの最初と最後のものを使い、間にあるものは"unused"なラインポインタとして再利用可能となります。ラインポインタは一つ4byteと些細な量のため、これが原因で何か問題が起こることはほぼないでしょう。ただし、塵も積もれば山となりますので注意したい点です。

また、INSERTやUPDATE処理がアボートされた結果できたガベージについては、同じページ内にUPDATE、DELETEされたレコードがない限り掃除がされません。そのため、大量のINSERTやUPDATE処理をアボートした場合、ガベージが充填したままのページができます。これらのページについては、HOTの掃除に頼ることができないというわけです。

上記は細かい注意点なのですが、これらに対してautovacuumなどでガベージのメンテナンスを補完することで、より最適で自動化された運用が可能となります。


【コラム】ガベージ回収の確認

ガベージの回収について実際にチェックしてみましょう。方法は、前回のコラムでご紹介したpg_stat_user_tablesの情報を監視する方法です。まず、以下の様なテストデータを作ります。

postgres=# CREATE TABLE hot_test (id int, name text);
postgres=# CREATE INDEX hot_test_index ON hot_test (id);
postgres=# INSERT INTO hot_test SELECT generate_series(1,100),
                                md5(clock_timestamp()::text);

そして、以下の様に、インデックスを張っていないnameカラムを更新していきます。

postgres=# UPDATE hot_test SET name='AAA' WHERE id=1;

pg_stat_user_tablesを見てみると、n_tup_hot_updがカウントアップされているため、ちゃんとHOT更新がされていることが分かります。しかし、数回の更新ではn_dead_tupが増えていくのみです。つまり、まだガベージの自動回収はされていません。16回目の更新までは以下の様な状態でした。

postgres=# SELECT relname, n_tup_upd, n_tup_hot_upd,
                  n_live_tup, n_dead_tup 
             FROM pg_stat_user_tables WHERE relname='hot_test';
-[ RECORD 1 ]----+----------
relname          | hot_test
n_tup_upd        | 16
n_tup_hot_upd    | 16
n_live_tup       | 100
n_dead_tup       | 16

17回目の更新で、ガベージ回収を実施すべき閾値(ページにもうあまり空き領域がない状態)を超えたため、n_dead_tupが減りました。

postgres=# SELECT relname, n_tup_upd, n_tup_hot_upd,
                  n_live_tup, n_dead_tup 
             FROM pg_stat_user_tables WHERE relname='hot_test';
-[ RECORD 1 ]----+----------
relname          | hot_test
n_tup_upd        | 17
n_tup_hot_upd    | 17
n_live_tup       | 100
n_dead_tup       | 1   ★

このように、ガベージが回収されて不要領域がリアルタイムに掃除されていることが分かります。 定期的にこれらの情報を監視することで、ガベージが順調に回収されているかどうかを確認できます。

 


さて、HOTの仕組みについて見てきました。やや難しい話になってしまいましたが、PostgreSQLの中でどんなことが行われているか、少しは明らかになったのではないでしょうか?仕組みとしてはシンプルに見えるかもしれませんが、データの整合性や一貫性が重要視されるDBMSでは、HOTの様な"データの位置情報変更"や"データ削除"の制御の改良を行うことは大変なことです。その甲斐もあって前回の様な効果が得られたわけです。

今回解説した仕組みを踏まえて、次回は「HOTの上手な使い方」という題目でお話をしていきたいと思います。