「PostgreSQLのJSONBをとりあえずGINインデックスで突っ込んでおけばOKでしょ?」
もし君がそう思っているなら、今日の話はきっと役に立つはずだ。確かに`jsonb`型は便利だ。スキーマレスな柔軟性は開発スピードを劇的に上げてくれる。でもね、本番環境でデータが数百万件を超えてきたとき、その「適当なインデックス」が足かせになって泣きを見るのは、決まって運用に入った後の君自身なんだ。
今日は、PostgreSQLのGINインデックスの中でも、特に使い分けが重要な`jsonb_ops`と`jsonb_path_ops`について、現場の知見を交えて解説するよ。
—
1. デフォルトの罠:jsonb_opsとは何か
PostgreSQLでGINインデックスを普通に作ると、こうなるよね。
CREATE INDEX idx_data_default ON my_table USING GIN (data);
これがデフォルトの`jsonb_ops`だ。このインデックスは、「JSONB内のすべてのキーと値を個別にインデックス化」する。
- 何ができるか: `?`, `?|`, `?&` といった演算子を使って、「このキーが存在するか?」「このキーの組み合わせは?」といったクエリを高速にさばける。
- 弱点: インデックスがデカくなる。本当にデカい。JSONの中身がネストしていればしているほど、インデックスのツリーは肥大化するんだ。
2. 隠れたエース:jsonb_path_opsの凄み
一方で、僕が実務でよく推すのが`jsonb_path_ops`だ。作り方はこう。
CREATE INDEX idx_data_path ON my_table USING GIN (data jsonb_path_ops);
これは何をしてくれるかというと、JSONの構造全体をハッシュ化してインデックスに格納する。
- メリット: `jsonb_ops`に比べてインデックスサイズが圧倒的に小さい。ケースによっては半分以下になることもある。ストレージ節約はもちろん、インデックスのツリーが小さくなるから、検索性能も向上するんだ。
- 最大の制約: 「キーの存在確認(`?`演算子など)」が使えなくなる。あくまで「このパスにこの値があるか(`@>`演算子)」という包含関係の検索に特化している。
—
3. どっちを選ぶべきか?トレードオフの境界線
「じゃあ、どっちがいいの?」って話になるよね。判断基準はシンプルだ。君がアプリケーションでそのカラムをどう叩いているか、それだけだよ。
`jsonb_ops` を選ぶべきケース
- `?` や `?&` 演算子を多用している。
- 「特定のキーが含まれているか」という存在確認がクエリのメインである。
- データの構造が比較的フラットで、インデックスサイズの増加があまり気にならない。
`jsonb_path_ops` を選ぶべきケース(おすすめ!)
- `@>` 演算子による絞り込みがメインである(ぶっちゃけ、これが一番多いよね)。
- データのネストが深く、インデックスサイズを抑えたい。
- パフォーマンスチューニングでインデックスのメモリヒット率を上げたい。
先輩からのアドバイス:
多くのWebアプリの検索機能は、結局のところ `WHERE data @> ‘{“status”: “active”}’` のような包含検索が大半だ。だったら、最初から`jsonb_path_ops`を選んでおく方が、長期的にはディスクI/Oの節約になって幸せになれることが多いよ。
—
4. 実践的な比較コード
実際にどうクエリが変わるか、軽く見てみよう。
— 包含検索:両方で使える(jsonb_path_opsの方が速い傾向にある)
SELECT FROM my_table WHERE data @> ‘{“user”: {“id”: 123}}’;
— キーの存在確認:jsonb_opsでしか使えない
— jsonb_path_opsでこれをやるとエラーになる!
SELECT FROM my_table WHERE data ? ‘tags’;
もし君のプロダクトで「時々キーの存在チェックもしたいけど、検索メインは包含検索だ」という欲張りなケースがあるなら、いっそのこと「包含検索用のGINインデックス」と「キーチェック用のB-treeインデックス(特定のキーを抽出した関数インデックス)」を分けるのも賢い選択だ。
最後に:データベースは「正直」だ
データベースのインデックス設計に魔法はない。インデックスを小さくすれば検索は速くなるし、柔軟性を持たせればサイズは肥大化する。
「とりあえず」で思考停止するんじゃなくて、「このデータにはどんなクエリが走るのか?」を一度立ち止まって考えてみてほしい。そうやって泥臭くチューニングした経験こそが、君をただのエンジニアから、一流のエンジニアに変えてくれるはずだ。
次は、実際に`pg_size_pretty(pg_relation_size(‘index_name’))`で、自分の環境のインデックスサイズを測ってみるところから始めてみようか。驚くほど変わるはずだよ。応援してる!
コメント