【テクニカル・上級編】 結合戦略: ハッシュ結合 – PostgreSQL

ハッシュ結合の深淵:PostgreSQLの「メモリ」を制する者がクエリを制す

PostgreSQLのクエリプランナと夜通し対峙していると、時折「なぜここでハッシュ結合を選んだのか?」と問いかけたくなる瞬間があるはずだ。ネステッドループの直感的な挙動や、マージ結合の整列された美しさに比べると、ハッシュ結合はどこかブラックボックスのように感じられるかもしれない。

しかし、大規模なデータセットを扱う現場において、ハッシュ結合を使いこなせるかどうかは、エンジニアとしての格を分ける境界線だ。今回は、PostgreSQLにおけるハッシュ結合の内部挙動を解剖し、パフォーマンストラブルの最前線で何が起きているのかを紐解いていこう。

—

ハッシュ結合の「静かなる」プロセス

ハッシュ結合の基本戦略はシンプルだ。まず、結合条件の小さい方(内側テーブル)をスキャンし、結合キーをハッシュ関数に通してメモリ上にハッシュテーブルを構築する。次に、外側のテーブルをスキャンし、同様にハッシュ化して、先ほど作ったテーブルをルックアップする。

ここで最も重要なのは、「ハッシュテーブルをどこに作るか」という点だ。

PostgreSQLは、`work_mem`の設定値を拠り所にして、このハッシュテーブルをメモリ上に展開しようと試みる。もしデータセットが小さければ、すべてがメモリ内で完結する「In-memory Hash Join」となり、極めて高いパフォーマンスを発揮する。CPUサイクルを効率的に使い、シーケンシャルなメモリ走査に近い速度で結合が完了するからだ。

「あふれ」がもたらす悲劇:Disk-based Hash Join

しかし、現実はそう甘くない。統計情報が狂っていたり、データが予想外に膨らんだりして、`work_mem`の制限を超えるとどうなるか。

PostgreSQLは「Batching」という戦略を採る。ハッシュテーブルをパーティション(バケット)に分割し、一部をメモリに保持しつつ、残りを一時ファイル(`base/pgsql_tmp/`配下)へ退避させる。このディスクI/Oが始まると、クエリの実行時間は指数関数的に悪化する。

現場で「なぜか特定のクエリが遅い」と感じる場合、`EXPLAIN (ANALYZE, BUFFERS)`を覗いてみてほしい。「Batches:」という行が見えたら要注意だ。特に、バッチ数が極端に多い場合は、メモリ不足によるディスクスワップが、あなたのデータベースの喉元にナイフを突きつけている状態だと言える。

パフォーマンストラブルシューティングの勘所

ハッシュ結合の性能問題を解決するための、僕なりのアプローチをいくつか共有する。

  • work_memの「一点集中」突破

`work_mem`をサーバー全体で上げすぎるとOOM Killerに殺される。特定のクエリだけが重いなら、セッション単位で `SET LOCAL work_mem = ‘256MB’;` のように一時的に引き上げるのが、運用上のセオリーだ。

  • 統計情報の鮮度を疑え

プランナがハッシュ結合を選択するのは「内側テーブルが小さい」と判断したからだ。しかし、`ANALYZE`が古くて実際は巨大なテーブルだったら?プランナはメモリ不足を予見できず、無謀なハッシュ結合を選んで爆死する。`pg_stats`を覗き、見積もりと実際の行数(`rows` vs `actual rows`)に乖離がないか確認してほしい。

  • 結合キーの型不一致という罠

意外と見落としがちなのが、結合キーのデータ型だ。`text`と`varchar`の比較や、暗黙のキャストが発生していると、ハッシュ計算のコストが跳ね上がるだけでなく、統計情報が正しく機能しないことがある。型は常に一致させておく。これが基本にして最強の最適化だ。

最後に:ハッシュ結合は魔法ではない

ハッシュ結合は強力だが、あくまで「等価結合(`=`)」に特化した手法だ。範囲検索や非等価結合が必要な場面では、プランナはハッシュ結合を諦め、ネステッドループやマージ結合にフォールバックする。

エンジニアとして大切なのは、ハッシュ結合を単なる「プラン」として眺めるのではなく、メモリという限られたリソースの上で、いかに効率的にデータを配置し、いかに速く検索するかという「物理的な戦い」として捉えることだ。

もし今、あなたのデータベースでハッシュ結合がディスクへ溢れ出しているなら、まずは`work_mem`のチューニングから手をつけてみてほしい。それが、クエリの応答速度を劇的に変える第一歩になるはずだ。

さて、次はどのプランを見に行こうか。深淵はまだまだ奥が深い。

コメント

タイトルとURLをコピーしました