CodeGym /課程 /SQL SELF /JSONB 資料的索引:使用 GIN 和 BTREE 索引...

JSONB 資料的索引:使用 GIN 和 BTREE 索引

SQL SELF
等級 38, 課堂 0
開放

第一個問題:我們為什麼要用 JSONB?JSONB 讓你可以用 JSON 格式存資料,結構超彈性。這超適合資料很複雜、會巢狀的情境(像是用戶 profile 裡面有一堆地址或設定)。跟單純的 JSON 不一樣,JSONB 是用 binary 格式存的,查詢跟篩選都快很多。

但如果沒加索引,查 JSONB 會慢到爆,尤其是表格有幾千幾百萬筆的時候。想像一下,有個用戶資訊表,每個人的設定都塞在 JSONB 欄位裡。你要找所有設定裡有某個值的用戶,沒索引的話,查詢會超吃資源。這時候索引就派上用場啦!

JSONB 索引:重點整理

PostgreSQL 支援兩種主要方式來幫 JSONB 加索引:

  1. GIN (Generalized Inverted Index) — 可以查 JSONB 裡的 key 跟 value。
  2. 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 索引文件在 這裡。有空記得去翻翻,裡面有更多細節跟範例。

2
任務
SQL SELF, 等級 38, 課堂 0
上鎖
為 JSONB 搜尋建立 GIN 索引
為 JSONB 搜尋建立 GIN 索引
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION