「UNLOGGEDテーブル」という劇薬――その快楽と代償について
PostgreSQLを長く触っていると、誰しも一度は「この書き込み負荷、なんとかならないか」という壁にぶつかります。特に、バッチ処理の中間テーブルや、一時的な分析用データセットを扱うとき、WAL(Write Ahead Log)の書き込みがボトルネックになって頭を抱える。
そんな時、目の前に現れる「UNLOGGED」という魔法のスイッチ。これをオンにした瞬間、パフォーマンスが劇的に跳ね上がるのを経験して、思わずニヤリとしたエンジニアも多いはずです。でも、ちょっと待ってください。その「劇薬」の副作用、本当に理解して使っていますか?
今日は、UNLOGGEDテーブルの内部構造に少し深く潜り込み、我々エンジニアがどこでリスクを負い、どこでメリットを享受すべきか、その境界線について語らせてください。
—
WALをスキップするということ
ご存知の通り、PostgreSQLがACID特性を担保できる最大の功労者はWALです。どんな変更も、まずはログに書き出し、永続性を保証する。しかし、UNLOGGEDテーブルはこのプロトコルを意図的にバイパスします。
内部的には、通常のテーブルと何が違うのか。最も決定的なのは、「データの更新がWALに記録されない」という一点です。これにより、ディスクへの物理的なI/Oが激減し、チェックポイントの負荷も緩和されます。特に書き込み頻度が高い処理において、この「ログ書き込みのオーバーヘッドがない」状態は、まさにエンジニアにとってのドーピングのようなものです。
クラッシュリカバリの冷徹な現実
ここからが本題です。UNLOGGEDテーブルのデータは、PostgreSQLがクラッシュした瞬間にどうなるか?
答えは非常にシンプルで冷酷です。「インスタンス再起動時に、データは初期化(TRUNCATE)される」。
これはバグではありません。設計思想です。PostgreSQLは、クラッシュ後のリカバリプロセスにおいて、UNLOGGEDテーブルを「信頼できないもの」と判断し、整合性を保つために初期化します。
ここで注意すべきなのが、「スタンバイサーバーへの影響」です。ストリーミングレプリケーション環境において、プライマリでUNLOGGEDテーブルを作成・操作しても、その変更はスタンバイには伝播しません。物理レプリケーションはWALを流すことで成り立っているからです。結果、スタンバイ側には「空のテーブル」が作られるか、あるいは参照時に予期せぬエラー(「テーブルが存在しません」など)を吐くことになります。この挙動を忘れて「なぜレプリカのデータが同期されないんだ!」と深夜に叫ぶのは、若手時代の通過儀礼としては少し痛すぎますよね。
どんな設計に持ち込むべきか
この特性を理解した上で、私が「UNLOGGED」を推奨するケースは、主に以下の2つに集約されます。
- 冪等性が完全に担保された一時的なバッチ処理
- 計算ロジックが再実行可能で、データが消えても「最初からやり直せばいい」と割り切れる場合。
- 読み取り専用のキャッシュテーブル
- 外部ソースからデータをロードし、読み取りにのみ使用する。万が一消えても、ソースから再ロードすれば復旧できる場合。
逆に、「消えては困るデータ」をUNLOGGEDに入れるのは、自ら地雷を踏みに行くようなものです。「パフォーマンス向上のためにUNLOGGEDにしたら、障害時にデータが消えてパニックになった」という話は、笑い話にはできません。
パフォーマンスチューニングの視点から
もしあなたが、UNLOGGEDテーブルを使ってもまだパフォーマンスが足りないと感じているなら、一度 `pg_stat_user_tables` を見てみてください。
UNLOGGEDだからといって、インデックス設計を疎かにしていい理由にはなりません。むしろ、WALの負荷がない分、インデックスの更新コストが全体に占める割合が相対的に大きくなります。`n_tup_ins` や `n_tup_upd` の統計と、インデックスの断片化状況を突き合わせ、本当に必要なインデックスだけを絞り込む。UNLOGGEDの恩恵を受けるなら、その分、インデックスのメンテナンスコストも極限まで削ぎ落とすのが、熟練エンジニアの流儀です。
最後に
技術とは、魔法ではありません。トレードオフを計算し、リスクを管理するための道具です。
UNLOGGEDテーブルは、PostgreSQLの堅牢な世界の中にある、唯一の「スリルを味わえる場所」かもしれません。そのリスクを制御し、システムのパフォーマンスを極限まで引き出せるなら、これほど頼もしい相棒もいないはずです。
皆さんのデータベース設計が、今日も明日も、強固で最適化されたものでありますように。では、また。
コメント