【実務・中級編】 JSONBインデックス戦略 – PostgreSQL

JSONBの沼にハマる前に。PostgreSQLで「爆速」を実現するインデックス戦略

どうも。最近、若手エンジニアから「JSONBを使っているんだけど、クエリがどうにも遅くて…」という相談を受けることが増えました。

PostgreSQLの`JSONB`は本当に便利ですよね。スキーマレスな柔軟性は、スピードが命のスタートアップや、変化の激しいマイクロサービスには欠かせない。でも、「とりあえず`JSONB`に入れとけばなんとかなるっしょ」と油断していると、ある日突然、クエリが死にます。

今日は、JSONBを実戦で使い倒すための「インデックスの最適解」について、現場の知見を少し共有しようと思います。

—

1. 基本の「き」:GINインデックスをどう貼るか

まず、JSONBの検索を高速化するなら「GIN(Generalized Inverted Index)」一択です。これは転置インデックスのようなもので、JSON内の各キーや値をバラバラに分解してインデックス化してくれます。

基本の構文はこうですね。

CREATE INDEX idx_data_gin ON users USING GIN (data);

これで `data @> ‘{“status”: “active”}’` のようなクエリが爆速になります。簡単ですよね。でも、ここからが本題です。

2. 賢い選択:`jsonb_path_ops` という隠し玉

デフォルトのGINインデックス、実は結構な「メタボ」になりがちなんです。インデックスサイズが肥大化すると、更新時のオーバーヘッドが無視できなくなります。

そこで提案したいのが `jsonb_path_ops` です。

CREATE INDEX idx_data_path_ops ON users USING GIN (data jsonb_path_ops);

なぜこれを使うのか?
デフォルトのGINは、JSON内の個々のキーと値をすべてインデックス化しますが、`jsonb_path_ops`は「パス(キーの階層構造)」をハッシュ化してインデックスを作ります。

  • メリット: インデックスが圧倒的に小さくなる。そして、検索速度が向上する。
  • デメリット: `@>(包含演算子)`しか使えない。`?`(キーの存在確認)などの演算子が使えなくなる。

実務レベルだと、ほとんどのクエリが「特定のキーの値を探す」ことなので、これで十分なケースがほとんどです。インデックスの肥大化に悩んだら、まずこれを疑ってください。

3. 「特定のキー」だけ狙い撃つB-treeインデックス

これ、意外と知られていないんですが、JSONBの特定のキーだけを抽出して、通常の「B-treeインデックス」を貼ることもできるんです。

例えば、`data` カラムの中に `user_id` があって、それに対する検索が頻繁に発生する場合。

CREATE INDEX idx_users_id ON users ((data->>’user_id’));

これがなぜ強いのか?
GINインデックスは万能ですが、特定の単一カラムを検索する場合、B-treeの方が圧倒的に効率的で軽量です。JSONB全体にGINを貼るよりも、「よく検索するキーは個別にB-treeで切り出す」のが、大規模システムにおける「大人のチューニング」です。

4. 先輩エンジニアからのアドバイス:バランスが全て

最後に、これだけは覚えておいてください。

「すべてのキーをGINでカバーしようとしないこと」です。

1. 頻繁に絞り込み条件に使うキー: `data->>’key’` でB-treeインデックスを貼る。
2. 検索パターンが予測不能なメタデータ: `jsonb_path_ops` を使ったGINインデックスを貼る。
3. そもそもJSONBにする必要はあるか?: パフォーマンスがボトルネックなら、JSONBを辞めてリレーショナルなカラムに切り出す勇気を持つ。

JSONBは強力な武器ですが、使いこなすには「データの中身が見えている」ことが前提です。まずは `EXPLAIN ANALYZE` を叩いて、インデックスがちゃんと効いているか確認する癖をつけましょう。

もし「自分のプロジェクトのこのクエリ、どうすればいい?」という悩みがあれば、いつでも相談してください。泥臭いチューニングこそ、データベースエンジニアの醍醐味ですからね。

それでは、また現場で!

コメント

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