第一個問題:我們為什麼要用 JSONB?JSONB 讓你可以用 JSON 格式存資料,結構超彈性。這超適合資料很複雜、會巢狀的情境(像是用戶 profile 裡面有一堆地址或設定)。跟單純的 JSON 不一樣,JSONB 是用 binary 格式存的,查詢跟篩選都快很多。
但如果沒加索引,查 JSONB 會慢到爆,尤其是表格有幾千幾百萬筆的時候。想像一下,有個用戶資訊表,每個人的設定都塞在 JSONB 欄位裡。你要找所有設定裡有某個值的用戶,沒索引的話,查詢會超吃資源。這時候索引就派上用場啦!
JSONB 索引:重點整理
PostgreSQL 支援兩種主要方式來幫 JSONB 加索引:
- GIN (Generalized Inverted Index) — 可以查
JSONB裡的 key 跟 value。 - BTREE — 適合簡單查詢跟排序。
這兩種各有特色,來細看一下。
GIN 索引 for JSONB
GIN 超強,能處理 array、text,還有 JSONB。它會把 JSONB 物件拆成一堆 key 跟 value,然後建一個特別的結構,讓你查資料快到飛起來。
GIN 用在 JSONB 的優點:
- key 跟 value 都能查。
- 巢狀結構也沒問題。
- 配合
@>、?、?|、?&這些 operator 查 key/value 超快。
假設有個 users 表,settings 欄位存每個人的設定,用 JSONB 格式。資料範例:
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name TEXT,
settings JSONB
);
INSERT INTO users (name, settings) VALUES
('Alice', '{"theme": "dark", "notifications": {"email": true, "sms": false}}'),
('Bob', '{"theme": "light", "notifications": {"email": false, "sms": true}}'),
('Charlie', '{"theme": "dark", "notifications": {"email": true, "sms": true}}');
現在我們想快點找到所有用 dark theme 的用戶(theme: dark)。先建索引:
CREATE INDEX idx_users_settings_gin ON users USING GIN (settings);
然後用 @> operator 查:
SELECT name
FROM users
WHERE settings @> '{"theme": "dark"}';
這樣 PostgreSQL 就會用 GIN 索引,查詢速度直接升級!
怎麼運作的?你在 JSONB 欄位建 GIN 索引時,PostgreSQL 會建一個「倒排」索引,也就是把所有 JSON 的 key 跟 value 都拆開記錄。像這個物件:
{"theme": "dark", "notifications": {"email": true, "sms": false}}
它會幫 theme、notifications.email、notifications.sms 跟它們的值都建索引。這樣你查單一元素就超快。
BTREE 索引 for JSONB
BTREE 是經典索引類型。如果你要整個 JSONB 物件比大小或排序,就用這個。但跟 GIN 不一樣,BTREE 不會拆解 JSON 內容。
BTREE 用在 JSONB 的優點:
- 很適合排序跟比對整個物件。
- 如果
JSONB當成「整塊」用(像是直接比對或找完全一樣的),速度很快。
來個 BTREE 索引範例。 假設 users 表常常要比對 settings 欄位跟某個物件:
{"theme": "dark", "notifications": {"email": true, "sms": false}}
先建索引:
CREATE INDEX idx_users_settings_btree ON users USING BTREE (settings);
然後可以直接查整個物件:
SELECT name
FROM users
WHERE settings = '{"theme": "dark", "notifications": {"email": true, "sms": false}}';
這個查詢就會用 BTREE 索引加速。
GIN 跟 BTREE 比一比
| 特性 | GIN |
BTREE |
|---|---|---|
| JSONB 物件拆解 | 會拆 key 跟 value | 不會,整個比 |
| 巢狀結構查詢 | 可以 | 不行 |
| 排序 | 不行 | 可以 |
| 索引大小 | 比較大 | 比較小 |
| 支援的 operator | @>、?、?|、?& |
=、排序 |
所以 GIN 適合複雜查詢,BTREE 則適合整個物件比對或排序。
該選哪個索引?
- 如果你要查
JSONB裡的單一 key 或 value,用GIN。 - 如果你要整個
JSONB物件比對或排序,用BTREE。
不過其實可以混著用!像是同一個欄位同時建 GIN 跟 BTREE 索引,這樣兩種查詢都能加速。
JSONB 索引常見錯誤
亂建沒必要的索引:不是每個 JSONB 欄位都要加索引。索引會佔空間,也會拖慢 insert 跟 update。
為很少用的 operator 建索引:不要只是覺得「好像該加」就加。先分析查詢,真的有幫助再加。
忽略 GIN 的特性:GIN 建索引比 BTREE 慢,特別是大表格時要注意。
實戰應用
在實際專案裡,JSONB 超適合彈性又動態的資料。像是:
- Web app 的用戶自訂設定。
- Log 資料,每種事件欄位都不一樣。
- 用 JSON 格式做 cache。
用 GIN 跟 BTREE 幫這些資料加索引,查詢效能會大提升。像面試時你就可以秀一下,怎麼靠索引讓系統快很多,特別是複雜資料結構的時候。
PostgreSQL 官方 JSON 索引文件在 這裡。有空記得去翻翻,裡面有更多細節跟範例。
GO TO FULL VERSION