最初の疑問:「なんでそもそもJSONB使うの?」って話。JSONBはJSONフォーマットでデータを保存できて、構造が柔軟なんだ。たとえば、ユーザープロフィールにアドレスや設定のリストがあるみたいな、複雑でネストしたデータにめっちゃ便利。普通のJSONと違って、JSONBはバイナリ形式で保存されるから、検索やフィルタリングがかなり速くなる。
でも、インデックスなしだとJSONBの検索は結構遅い。特にテーブルに何千、何百万行もあるときはヤバい。例えば、ユーザー情報のテーブルがあって、各ユーザーの設定をJSONBで保存してるとする。インデックスなしで特定の設定値を持つユーザーを探すのは、めっちゃリソース食う作業。そこでインデックスの出番ってわけ!
JSONBのインデックス作成:大事なポイント
PostgreSQLでJSONBをインデックス化するには、主に2つの方法があるよ:
- GIN (Generalized Inverted Index) —
JSONBの中のキーや値で検索する用。 - BTREE — シンプルな検索やソート用。
それぞれ特徴があるから、詳しく見ていこう。
JSONB用のGINインデックス
GINは強力なインデックスで、配列やテキスト、JSONBデータにも使える。JSONBオブジェクトの中身をキーと値に分解して、素早く検索できる特別な構造を作ってくれるんだ。
JSONBにGINを使うメリット:
- キーでも値でも検索できる。
- ネストした構造にも対応。
@>、?、?|、?&みたいな演算子(キーや値のフィルタリング)が速くなる。
例えば、usersテーブルがあって、settingsカラムにユーザー設定をJSONBで保存してるとする。データ例はこんな感じ:
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name TEXT,
settings JSONB
);
INSERT INTO users (name, settings) VALUES
('Alice', '{"theme": "ダーク", "notifications": {"email": true, "sms": false}}'),
('Bob', '{"theme": "ライト", "notifications": {"email": false, "sms": true}}'),
('Charlie', '{"theme": "ダーク", "notifications": {"email": true, "sms": true}}');
今度は「ダーク」テーマのユーザーをサクッと探したい。まずはインデックスを作成:
CREATE INDEX idx_users_settings_gin ON users USING GIN (settings);
次に@>演算子で検索:
SELECT name
FROM users
WHERE settings @> '{"theme": "ダーク"}';
これでPostgreSQLはGINインデックスを使って、検索がめっちゃ速くなる。
どうやって動いてるの?JSONBカラムにGINインデックスを作ると、PostgreSQLは「逆引き」インデックスを作る。つまり、JSONの全キーと値ごとに個別のエントリを作るんだ。例えば:
{"theme": "ダーク", "notifications": {"email": true, "sms": false}}
このオブジェクトからtheme、notifications.email、notifications.smsとその値でインデックス化される。だから個々の要素の検索が爆速になる。
JSONB用のBTREEインデックス
BTREEはクラシックなインデックス。JSONBオブジェクト全体を比較したり、ソートしたいときに使う。でもGINと違って、JSONオブジェクトの中身までは分解しない。
JSONBにBTREEを使うメリット:
- オブジェクトの比較やソートに最適。
JSONBを「かたまり」として使う場合(他のオブジェクトと比較したり、特定の値と一致する行を探すとき)は速い。
使い方の例を見てみよう。 例えばusersテーブルで、settingsカラムを特定のオブジェクトとよく比較する場合:
{"theme": "ダーク", "notifications": {"email": true, "sms": false}}
まずはインデックスを作成:
CREATE INDEX idx_users_settings_btree ON users USING BTREE (settings);
これでオブジェクト比較のクエリができる:
SELECT name
FROM users
WHERE settings = '{"theme": "ダーク", "notifications": {"email": true, "sms": false}}';
このクエリはBTREEインデックスを使って速くなるよ。
GINとBTREEの比較
| 特徴 | GIN |
BTREE |
|---|---|---|
JSONBオブジェクトの分解 |
はい、キーと値に分解する | いいえ、全体で比較 |
| ネスト構造の検索 | はい | いいえ |
| ソート | いいえ | はい |
| インデックスサイズ | 大きい | 小さい |
| 対応演算子 | @>, ?, ?|, ?& |
=, <, >など |
つまり、GINは複雑なクエリ向き、BTREEはオブジェクト全体の比較やソート向きってこと。
どっちのインデックスを選ぶ?
JSONBの中のキーや値で検索したいならGINを使おう。JSONBオブジェクト全体を比較したりソートしたいならBTREEがいいよ。
でも、両方のインデックスを同じカラムに作るのも全然アリ!テーブルで両方のクエリが必要なら、GINとBTREE両方作っちゃおう。
JSONBインデックスでありがちなミス
不要なインデックスの作成:全部のJSONBフィールドにインデックスを貼るのはやめとこう。インデックスは容量を食うし、INSERTやUPDATEも遅くなる。
あまり使わない演算子のインデックス化:「なんとなく正しそう」って理由だけでインデックスを作るのはNG。クエリをちゃんと分析して、本当に速くなるところだけインデックスを使おう。
GINの特徴を無視する:GINはBTREEよりインデックス作成に時間がかかることもある。大きなテーブルでは特に注意しよう。
実践での使いどころ
JSONBは、データが柔軟で動的なリアルなプロジェクトで役立つよ。例えば:
- ユーザー設定があるWebアプリ。
- イベントごとに違うフィールドを持つログの保存。
- JSON形式でのデータキャッシュ。
こういうデータをGINやBTREEでインデックス化すると、クエリのパフォーマンスがめっちゃ上がる。面接とかでも、「複雑なデータ構造にインデックスを追加してシステムを速くした」って話せるとカッコいいよ。
PostgreSQLの公式JSONインデックスドキュメントはここで見れる。困ったらチェックしてみて!
GO TO FULL VERSION