JSONBは「逃げ道」じゃない。PostgreSQLのjsonpathを使いこなすための戦略的思考
PostgreSQLを長年触っていると、「JSONBはとりあえず放り込んでおけばいい」という甘い誘惑に駆られることがあります。もちろん、スキーマレスの柔軟性は魅力的です。しかし、いざ数百万件規模のデータから特定のネストされたフィールドを抽出する段になると、とたんにパフォーマンスが牙を剥く。
そんなとき、多くのエンジニアは「まあ、JSONBだしこんなものか」と諦めてしまう。でも、それはまだSQL/JSONパス言語(jsonpath)のポテンシャルを使い切れていないだけかもしれません。
今日は、jsonpathの真価と、それがどうやってDBの内部で処理されているのか、そしてどうすれば「遅いJSONクエリ」を「爆速」に変えられるのか、その深淵を覗いてみましょう。
—
jsonpathがもたらした「検索の流儀」
PostgreSQL 12で導入されたSQL/JSONパス言語は、従来の`->>`演算子を多用する泥臭いクエリから、我々を解放してくれました。
SELECT jsonb_path_query(data, ‘$.orders[].items[] ? (@.price > 100).name’)
FROM sales;
このクエリを見て、「結局フルスキャンでしょ?」と思ったあなた。半分正解で、半分は損をしています。`jsonb_path_query`は、単なる抽出関数ではありません。これは、PostgreSQLのクエリエンジンが「データ構造を深く理解するためのインターフェース」なんです。
内部アーキテクチャ:なぜ「パス」は速いのか
`jsonb_path_query`が実行される際、内部では複雑なJSONBツリーを走査するための「パス探索エンジン」が動きます。
注目すべきは、JSONBがディスク上で「バイナリ形式」で保持されているという点です。文字列解析を伴う他のDBと異なり、PostgreSQLはJSONBを独自のバイナリフォーマットに変換して格納しています。jsonpathを実行すると、このバイナリ構造を効率的にたどるための反復子が呼び出されます。
つまり、`jsonpath`は単に「JSONをパースしている」のではなく、「バイナリツリー上の特定のノードにショートカットしている」のです。
パフォーマンスの「分水嶺」:インデックス設計の裏側
ここが本題です。多くのエンジニアが犯す最大のミスは、「JSONBの列にGINインデックスさえ貼れば、どんなクエリも速くなる」と思い込んでいること。
残念ながら、標準的なGINインデックスは「JSON内のキーや値の存在確認」には滅法強いですが、jsonpathの複雑な条件(特に範囲検索や論理演算)を完全にカバーできるわけではありません。
1. GINインデックスの構成を見直す
もし、`jsonpath`で頻繁に特定のパスを検索するなら、`jsonb_path_ops`演算子クラスを検討してください。
CREATE INDEX idx_sales_data ON sales USING GIN (data jsonb_path_ops);
通常の`jsonb_ops`がハッシュ化されたキーと値を格納するのに対し、`jsonb_path_ops`は「データ内のパス」をハッシュ化します。これにより、インデックスサイズは小さくなり、クエリ実行時の検索効率が飛躍的に向上します。これは、複雑なJSON構造を持つドキュメントを扱う際の「最適解」です。
2. 生成カラム(Generated Columns)という逃げ道
どうしてもクエリが遅いなら、無理にjsonpathで計算せず、頻出するフィールドを生成カラムとして切り出し、そこにB-treeインデックスを貼る。これが最も「エンジニアらしい」現実的な解決策です。「JSONBのまま扱うことに固執しない」という判断も、また高度なモデリングです。
トラブルシューティング:クエリが重いときの「儀式」
もしクエリが遅いと相談されたら、まずは `EXPLAIN (ANALYZE, BUFFERS)` を叩くはずです。その時、注目すべきは以下の2点です。
- Bitmap Heap Scanが長すぎないか?
インデックスが効いていても、ヒット数が多すぎるとヒープページへのランダムアクセスがボトルネックになります。`data`のサイズが大きすぎる場合は、不要なフィールドを削除して圧縮率を高める検討が必要です。
- jsonpathの記述は効率的か?
`$.items[].price` を見つけるのに、`$.`(ワイルドカード)を多用して深層探索させていませんか?パスはなるべく具体的に指定する。深さを限定するだけで、内部的な走査コストは劇的に下がります。
最後に:JSONBは「箱」ではなく「木」である
JSONBは、ただのデータの器ではありません。それはPostgreSQLという強固なRDBの中に同居する、もう一つの「木構造データモデル」です。
jsonpathを使いこなすということは、その「木」の成長の仕方を理解し、インデックスという「道」を適切に整備してあげること。そうすれば、JSONBはもはや「とりあえずの逃げ道」ではなく、複雑なドキュメントを高速に捌くための「強力な武器」に変わります。
ぜひ、皆さんのプロジェクトの実行計画を覗いてみてください。そこには、まだ最適化されていない「道」が隠れているはずですから。
コメント