なぜPostgreSQLは「ハッシュ結合」を愛するのか?―現場で役立つ内部構造のハナシ
「PostgreSQLのパフォーマンスがいまいちだな……」そう思って `EXPLAIN ANALYZE` を叩いたとき、真っ先に目に入るのが結合(Join)の方式ですよね。
Nested Loop、Merge Join、そして今日解説するHash Join。
特にデータ量が増えてくると、PostgreSQLは好んでこの「ハッシュ結合」を選択します。なぜこれほどまでに優秀なのか、そしてどこに落とし穴があるのか。現場の知見を交えて深掘りしてみましょう。
—
1. ハッシュ結合の仕組み:直感的な理解
ハッシュ結合を一言で言うなら、「一方を丸ごとメモリにぶち込んで、辞書引きする」という作戦です。
1. Buildフェーズ(ハッシュテーブル構築):
結合条件の小さい方のテーブルをスキャンし、結合キーをハッシュ関数にかけます。その結果を元にメモリ上にハッシュテーブルを作ります。これが「辞書」の役割を果たします。
2. Probeフェーズ(探索):
もう一方のテーブルをスキャンしながら、各行の結合キーを同じハッシュ関数にかけます。その結果をさっき作った「辞書」に照らし合わせ、一致する行をすかさず引っ張り出します。
Nested Loopが「片方のテーブルの1行ごとに、もう片方を全走査して探す(=時間がかかる)」のに対し、ハッシュ結合は「辞書引き」なので計算量はほぼO(N+M)。これが爆速の理由です。
—
2. 実践的な例:こんな時に選ばれる
例えば、数百万件の注文データ(`orders`)と、数万件のカテゴリマスタ(`categories`)を結合するとしましょう。
EXPLAIN ANALYZE
SELECT o.id, c.name
FROM orders o
JOIN categories c ON o.category_id = c.id;
PostgreSQLのオプティマイザは、ここで`categories`をメモリに乗せる「ハッシュテーブル」として選びます。小さい方をメモリに置くのが鉄則だからです。
ここで注意!「work_mem」という名の地雷
ここで現場のエンジニアとして一番伝えたいのが、`work_mem`の設定です。
もしハッシュテーブルがメモリ(`work_mem`)に入り切らないとどうなるか?PostgreSQLはディスクへ溢れ出します(これを「バケットの流出」と呼びます)。こうなると、せっかくのハッシュ結合がディスクI/Oの嵐に巻き込まれ、Nested Loopより遅くなることだってあります。
アドバイス:
`EXPLAIN ANALYZE`の出力を見てください。
`Batches: 1` なら理想的ですが、`Batches: 2` 以上になっていたり、`Disk: xxxkB` と表示されていたら、そのクエリはメモリ不足で苦しんでいます。その場合は、セッション単位で `SET work_mem = ’64MB’;` のように一時的に引き上げてテストしてみてください。劇的に改善することがありますよ。
—
3. いつハッシュ結合を疑うべきか?
「ハッシュ結合は最強!」と思われがちですが、実は苦手なケースもあります。
- 不等号結合(非等価結合):
`JOIN ON a.val > b.val` のような場合、ハッシュ関数は使えません。このときはNested LoopやMerge Joinが選ばれます。
- ソート済みの結果が欲しいとき:
ハッシュ結合はハッシュ化するため、結果の順序はバラバラです。もしその後に `ORDER BY` が続くなら、Merge Joinの方がトータルコストが安いケースがあります。
—
まとめ:現場で活かすための3ステップ
1. まずは `EXPLAIN ANALYZE` を見る習慣をつける。
「Hash Join」という文字だけでなく、その下の「Batches」と「Disk」の文字に注目する。
2. `work_mem` を意識する。
サーバー全体のメモリを食い過ぎない範囲で、重いバッチ処理があるセッションだけ調整する勇気を持つ。
3. オプティマイザを信じつつ、疑う。
データ分布が偏っていると、たまにオプティマイザが「ハッシュ結合」を過信して選んでしまうことがあります。そんな時はインデックスを見直し、統計情報(`ANALYZE`)を更新してあげるのが、エンジニアの腕の見せ所です。
ハッシュ結合は、PostgreSQLが誇る強力な武器です。その特性さえ理解していれば、DBの挙動が手に取るようにわかるようになりますよ。
さて、次は「Merge Joinがなぜ特定の条件下で最強なのか」について話しましょうか。……と、その前に、まずは手元の `EXPLAIN` 結果を眺めてみてください。きっと新しい発見があるはずです。
コメント