やあ、今日もデータベースと格闘してるかい?
PostgreSQLを触っていると、どうしてもB-treeインデックスばかりに頼りがちになるよね。まあ、それも仕方ない。とりあえずB-treeを貼っておけば大抵のケースはなんとかなるからね。
でも、パフォーマンスの限界を突き詰めようとすると、ふと「ハッシュインデックス」の存在が頭をよぎる瞬間があるはずだ。今回は、この少しクセがあるけれど、ハマれば強力なハッシュインデックスについて、実戦的な話をしようと思う。
—
ハッシュインデックスって結局なんなの?
一言で言うなら、「等価比較(=)に特化した、超高速なルックアップマシン」だ。
B-treeがデータをソートしてツリー構造で保持するのに対し、ハッシュインデックスは名前の通り「ハッシュ関数」を使って値をバケットに振り分ける。
- B-tree: 範囲検索(`<` や `>`)ができるけど、ツリーを辿る分だけオーバーヘッドがある。
- ハッシュ: 範囲検索は一切できない。でも、特定のキーに対する検索速度は、計算量 $O(1)$ に限りなく近い。
昔のPostgreSQLではハッシュインデックスはWALに書き込まれず、クラッシュリカバリができないという「おもちゃ」のような時期もあったんだけど、今のPostgreSQL(v10以降)はしっかり堅牢になっている。だから、実戦投入しても全然怖くないんだ。
どんな時に使うべきか?
「じゃあ、全部ハッシュインデックスにすれば速くなるの?」なんて聞かないでくれよ(笑)。答えはもちろんNoだ。
ハッシュインデックスが真価を発揮するのは、以下のようなケースだ。
1. インデックスサイズを抑えたい時
B-treeインデックスはキーが長くなると肥大化しやすい。ハッシュインデックスは値をハッシュ値に変換して保持するため、キーのサイズに関わらず(ある程度は)コンパクトに収まる可能性がある。
2. 完全一致検索しかしないカラム
IDやUUID、あるいはハッシュ化されたトークンなど、「`WHERE key = ‘…’`」というクエリしか投げないなら、B-treeよりもハッシュの方がメモリ効率や検索速度で優位に立てる。
実践:実際に使ってみる
使い方はシンプルだ。`USING HASH`を指定するだけ。
CREATE INDEX idx_user_token_hash ON users USING HASH (token);
これで、この`token`カラムに対してはハッシュインデックスが働くようになる。
もし、今運用しているDBの特定のテーブルで、巨大な文字列カラムに対する完全一致検索が多くて、インデックスが肥大化して困っているなら、一度これを試してみる価値はあるよ。
注意点:ここだけは忘れないでくれ
現場のエンジニアとして、これだけは釘を刺しておきたい。
- 範囲検索はできない: `WHERE token > ‘abc’` のようなクエリを投げた瞬間、このインデックスは無視されてフルスキャンが走る。EXPLAINコマンドを叩いて、ちゃんと意図したインデックスが使われているか確認する癖をつけておこう。
- マルチカラムインデックスは不可: 現状、ハッシュインデックスは単一カラムにしか貼れない。複合キーを使いたい場合はB-tree一択だ。
- 排他制御: 基本的にハッシュインデックスはB-treeよりロックの競合が少ないと言われているけれど、超高頻度で書き込みが発生するテーブルに貼る場合は、必ず負荷テストをしてから本番に入れてくれよ。
まとめ:魔法の杖ではないけれど、強力な武器
ハッシュインデックスは万能じゃない。でも、「何でもB-treeで解決しようとしない」という姿勢が、データベースエンジニアとしての腕を上げるんだ。
「このカラムは範囲検索なんて一生しないな」と確信が持てるなら、ハッシュインデックスを検討する。その選択肢を持っているだけで、DB設計の引き出しはグッと広がるはずだ。
もし次にパフォーマンスチューニングで頭を抱えることがあったら、一度B-treeからハッシュへ切り替えてみて、`EXPLAIN ANALYZE`の結果を眺めてみてほしい。きっと、新しい発見があるはずさ。
それじゃ、また現場で会おう。健闘を祈る!
コメント