初學者入門:什麼是 JSONB?

更新日期: 2025 年 3 月 24 日

可以開啟中文 CC 字幕

在現代網路應用中,前端與後端之間常透過 JSON(JavaScript Object Notation)格式來傳遞資料。

當我們把這些資料儲存到資料庫時,傳統的關聯式資料庫會要求我們將資料結構拆成表格。

然而,這對開發效率來說可能並不友善。

這時候,JSONB 的出現,就像是一座橋梁,讓開發者能更方便地在關聯式資料庫中儲存、查詢類似 NoSQL 的彈性資料格式。

那麼,什麼是 JSONB?它和一般 JSON 有什麼不同?什麼情境適合使用?這篇文章將帶你一步一步搞懂。


什麼是 JSONB?

JSON 與 JSONB 的基本概念

在 PostgreSQL(Postgres)這套功能強大的關聯式資料庫系統中,JSONB 是一種用來儲存 JSON 結構資料的資料型別。

要理解 JSONB,我們需要先從它的名稱來拆解:

JSON(JavaScript Object Notation)

是一種輕量級的資料交換格式,它以 key-value(鍵值對)的形式儲存資料,類似於一個字典或物件,廣泛使用於前後端資料交換。

舉個例子,一個代表使用者資訊的 JSON 可能會長這樣:

{
  "name": "Alice",
  "age": 30,
  "hobbies": ["reading", "hiking"]
}

這種格式的好處是:結構彈性大,可以自由加入或省略欄位,非常適合儲存動態變化的資料。

B(Binary)

這裡的 B 代表 Binary(二進位格式),表示 PostgreSQL 在處理 JSON 時,會把它轉成一種內部的二進位儲存方式。

這個轉換過程會對資料進行編碼與優化,使得後續在查詢、過濾、比對時更有效率。

換句話說,JSONB 並不是單純地儲存 JSON 文字內容,而是將 JSON 結構「解析並轉換」為一種更容易被資料庫處理的格式。

這讓我們能夠:

  • 使用 SQL 語法快速查詢 JSON 裡的欄位
  • 對 JSONB 欄位建立索引
  • 篩選資料時效率大幅提升

💡 舉個比喻幫助理解:

想像你去超市買菜。

  • JSON 就像你把所有的東西隨手丟進購物袋裡,沒有整理。東西是都有啦,但每次要找某樣東西(像是雞蛋)時,你得把整個袋子打開,翻一翻、找一找,才能確定雞蛋在不在裡面。
  • JSONB 則像是你一回家就把所有食材按照分類放進冰箱、櫃子或抽屜。下次你要拿雞蛋時,走到冰箱、打開蛋盒,一秒就找到了。

這個差別在於:

  • JSON 的資料是「照原樣」儲存的,資料庫每次查詢時都要重新「翻找」資料,耗時耗力。
  • JSONB 則是資料庫先幫你整理好,未來要查什麼資料就像去抽屜拿東西一樣快速、有效率。

JSON vs JSONB:差異比較

雖然 JSONJSONB 都能儲存類似的資料,但它們在處理方式上其實有不少差異。以下用表格來說明兩者的主要不同:

特性JSONJSONB
儲存格式純文字(原樣儲存)二進位格式(解析後儲存)
查詢效能較慢,需即時解析字串較快,因為預先轉換格式
可否建立索引是(可使用 GIN 索引等)
保留 key 的順序是(照原始順序)否(會自動排序)
重複 key 是否允許是(照原樣保留)否(只保留最後一個)
插入速度較快(不需轉換格式)較慢(需先編碼處理)

更進一步說明這些差異:

查詢效能

當你用 JSON 儲存資料後,每次查詢都得臨時解析整段文字,才能找到對應欄位,效能自然比較差。

JSONB 是事先幫你轉成結構化的資料,資料庫可以更快地定位你要的內容。

索引支援

JSONB 支援建立索引(例如 GIN index),這對於大量資料查詢是非常關鍵的優勢。

JSON 不支援索引,只能靠全文掃描,非常沒效率。

key 順序與重複

JSON 保留你原本的鍵順序與重複項目(雖然實務上不建議重複 key),但 JSONB 為了優化,會自動排序 key 並移除重複項,保留最後一個值。

這點在儲存與序列化時,會導致資料格式略有不同。

插入速度

因為 JSONB 需要先解析再存進資料庫,所以插入時速度略慢,JSON 是直接以文字方式儲存,速度會比較快。

但這通常只在大量寫入、批次資料匯入時才有明顯差異。


為什麼使用 JSONB?

在現代應用中,資料的型態與結構越來越多元,有時候很難一開始就設計出完美的資料庫欄位結構。

而傳統的關聯式資料庫雖然穩定且功能強大,但在面對「結構不固定」或「高度彈性」的資料時,往往顯得不夠靈活。

這時候,JSONB 就像是一個折衷又強大的解決方案,結合了 NoSQL 的靈活性與 SQL 的強大查詢能力。

彈性資料結構:資料未定型時的救星

在開發專案的初期,資料結構常常還沒定型,尤其是當你開發 MVP(最小可行產品)或原型階段時。舉個例子:

假設你在開發一個會員系統,但每個會員所需要的資料欄位可能會依身份、地區、功能權限而有所不同。

有些會員有「生日」、「興趣」,有些則有「公司資訊」、「職稱」,這時如果硬要拆表或建立固定欄位,不但繁瑣,也限制了資料的靈活性。

這時候,你可以利用 JSONB 儲存這些變動性高的欄位

profile JSONB

將所有不固定的資料丟進 profile 欄位中,像這樣:

{
  "nickname": "小明",
  "birthday": "1995-06-12",
  "interests": ["籃球", "旅行"],
  "company": {
    "name": "ABC Corp",
    "title": "工程師"
  }
}

當你的業務需求越來越明確、資料結構逐漸穩定後,你再將常用或查詢頻繁的欄位拆出來成為獨立欄位或子表格,也不遲。

這種做法讓你在早期開發時不被資料庫結構綁死,可以快速驗證功能、彈性調整欄位設計。

結合關聯式與 NoSQL 的優勢:雙贏不是夢

NoSQL 資料庫(例如 MongoDB)最大的優勢就是結構自由。

你可以隨意定義文件格式,每筆資料長得不一樣也沒關係,這在快速開發時非常方便。

但缺點是:不容易跨表查詢、沒有嚴謹的交易(transaction)控制、資料一致性較難維護。

而傳統關聯式資料庫(像 PostgreSQL)則有以下優點:

  • 支援 ACID 交易(確保資料一致性)
  • 有強大的 SQL 查詢語法
  • 易於做跨表關聯與 JOIN 操作

JSONB 的出現,讓你不用二選一。

它讓你在 PostgreSQL 裡:

  • 用 JSONB 儲存彈性資料(像是 NoSQL)
  • 同時享有 SQL 的查詢功能與 ACID 的保障

簡單來說,就是把 NoSQL 的自由與 SQL 的強大合而為一,非常適合用在「資料結構半固定」的場景。例如:

  • 儲存用戶自訂欄位(如個人設定、自介)
  • 多語系欄位(例如同一欄位內含不同語言版本的內容)
  • 變動性高的 API 輸入資料

支援高效查詢與索引:不是只能當資料倉庫

許多開發者以為 JSON 結構儲存在資料庫後,只能當純資料存放用,沒辦法有效查詢。

JSONB 改變了這一切

PostgreSQL 不只支援對 JSONB 欄位進行查詢,還提供了許多實用的操作符號與函式,像是:

  • ->:取出某個欄位的 JSON 結構
  • ->>:取出某個欄位的純文字值
  • ?:判斷某個 key 是否存在
  • @>:判斷某個 JSON 結構是否包含另一個結構

範例查詢:找出所有有興趣欄位中包含 “閱讀” 的會員:

SELECT * FROM users
WHERE profile->'interests' ? '閱讀';

此外,PostgreSQL 也支援針對 JSONB 欄位建立高效能的索引,像是:

  • GIN index(Generalized Inverted Index):可加速 key-value 搜尋的速度
  • BTREE index(對純值排序有用)

透過這些工具,你可以像查一般欄位一樣查 JSONB 裡的欄位資料,大幅提升查詢效能。

這讓 JSONB 不只是方便儲存,更能夠在需要的時候,幫你高效處理大量結構不一的資料。


如何使用 JSONB?

學會使用 JSONB 不只是把 JSON 丟進資料庫而已,更重要的是:你要知道怎麼定義欄位、插入資料、撰寫查詢、建立索引,才能真正發揮它的強大功能。

以下是一個完整的入門教學,從建表到查詢、索引通通都有。

定義 JSONB 欄位:從建表開始

要使用 JSONB,第一步是先在資料表中定義一個欄位,其資料型別為 JSONB

舉例來說,如果你想建立一個 users 表格,並讓每位使用者的個人資料(如姓名、年齡、興趣等)用 JSONB 儲存,可以這樣寫:

CREATE TABLE users (
  id SERIAL PRIMARY KEY,
  profile JSONB
);

說明:

  • id SERIAL PRIMARY KEY:建立一個自動遞增的主鍵欄位。
  • profile JSONB:宣告一個欄位名稱為 profile,資料型別為 JSONB,可存放任意 JSON 結構。

你也可以視需求增加其他欄位,例如 created_at(建立時間)、email 等。

💡 貼心提醒:PostgreSQL 也支援 JSON 型別,但如前文所述,通常建議預設使用 JSONB,因為它支援索引與高效查詢。

插入資料:存進 JSON 結構

當你有了 JSONB 欄位,就可以將 JSON 格式的資料直接插入。以下是範例 SQL:

INSERT INTO users (profile)
VALUES ('{
  "name": "Alice",
  "age": 30,
  "hobbies": ["reading", "hiking"]
}');

這段 SQL 做了什麼?

  • 將一個 JSON 結構(包含 nameagehobbies)寫入 profile 欄位中。
  • PostgreSQL 會自動將這段字串解析為 JSON,並轉換成 JSONB 格式儲存。

注意事項:

  • 插入的 JSON 格式必須為合法 JSON(例如:key 必須用雙引號 " 包住,不能用單引號 ')。
  • 若你在應用程式中使用 ORM(像是 Sequelize、TypeORM、Django ORM),也能直接用物件方式傳入 JSON,資料庫端自動轉換。

查詢 JSONB 資料:從資料裡挖出你要的資訊

使用 JSONB 最實用的地方,就是你可以用 SQL 語法來查 JSON 裡的欄位,不必整段抓出來慢慢處理。

✅ 範例 1:查詢興趣包含 “reading” 的使用者

SELECT * FROM users
WHERE profile->'hobbies' ? 'reading';

解析:

  • profile->'hobbies':取出 profile 裡的 hobbies 欄位(回傳 JSON 陣列)。
  • ? 'reading':判斷該陣列中是否包含值為 'reading' 的元素。

結果會找出所有興趣中包含閱讀的使用者。

✅ 範例 2:查詢年齡大於 25 的使用者

SELECT * FROM users
WHERE (profile->>'age')::int > 25;

解析:

  • profile->>'age':取出 profile 中的 age 值,並轉成字串(因為 ->> 回傳文字)。
  • ::int:用 PostgreSQL 的型別轉換,把字串轉為整數。
  • > 25:篩選條件。

這樣就能查出所有年齡超過 25 歲的使用者,即使 age 是存在 JSON 裡,也能用數值比較。

✅ 範例 3:查詢存在某個 key 的資料

SELECT * FROM users
WHERE profile ? 'company';

這會找出所有 profile 裡面有 company 這個欄位的使用者,無論它的值是什麼。

建立 JSONB 索引:讓查詢飛起來!

當資料量變大(例如上萬筆使用者),如果沒建立索引,查詢效率會大幅下降。幸好 PostgreSQL 支援針對 JSONB 欄位建立索引。

✅ 建立 GIN 索引(最常用的 JSONB 索引)

CREATE INDEX idx_profile ON users USING GIN (profile);

解說:

  • USING GIN:使用 Generalized Inverted Index,特別適合用在 JSONB 的結構查詢中。
  • 索引會分析 profile 裡每個 key-value 的內容,幫你建立快速查找路徑。

這樣做的好處:

  • 加速使用 ?@> 等操作符的查詢
  • 當你的 JSON 結構越來越複雜,查詢條件越來越多時,效能不會崩潰

進階技巧(可選):

如果你知道查詢只會用到某些 key,也可以建立 部分索引,只針對這些欄位加速查詢。例如:

CREATE INDEX idx_profile_name ON users ((profile->>'name'));

這個索引只針對 profile 裡的 name 欄位進行優化,適合用在你常常根據姓名查找的場景。


使用 JSONB 的注意事項:強大之餘也有眉角

雖然 JSONB 帶來極高的彈性與查詢效能,但它畢竟不是萬能,某些行為與特性可能會讓你在不注意的情況下踩雷。

因此,了解 JSONB 的限制和潛在問題,對於開發者來說至關重要。

不保留 key 的順序:無法用來呈現順序敏感的資料

在一般的 JSON 格式中,key(欄位)是按照原始輸入的順序儲存的。例如:

{
  "first_name": "Alice",
  "last_name": "Chen",
  "age": 30
}

如果你把這段資料存成 JSON 格式,它的順序就是:first_name → last_name → age。

但在 JSONB 中,為了讓資料查詢與索引更有效率,PostgreSQL 會在儲存資料時自動將 key 排序(通常依字母順序),因此上述資料在資料庫內部可能會被重組成:

{
  "age": 30,
  "first_name": "Alice",
  "last_name": "Chen"
}

這在功能上完全不影響查詢或資料正確性,但若你的前端應用有「欄位順序」的需求(例如表單欄位依照順序渲染、或自訂欄位順序顯示),那麼使用 JSONB 儲存就可能會產生混亂。

建議做法

  • 若你有順序需求,可以在 JSONB 裡加入一個 order 屬性明確定義順序。
  • 或者使用純 JSON 儲存(若不需要查詢),或將顯示邏輯交由應用程式層處理。

無法保留重複 key:重複欄位會被覆蓋掉

根據 JSON 規範,雖然你技術上可以撰寫一段有重複 key 的 JSON,例如:

{ "a": 1, "a": 2 }

但在多數程式語言與資料庫中,這樣的結構會被視為不合法或不可預測。

JSON 格式會保留這樣的原始結構,但 JSONB 為了資料一致性與查詢準確性,只會保留最後一個 key 的值

上面的例子儲存進 JSONB 時,會變成:

{ "a": 2 }

這可能會導致資料遺失而不自知,尤其當你的 JSON 是從外部來源取得或透過 API 自動生成時。

建議做法

  • 永遠避免使用重複 key 的 JSON 結構。
  • 可在送出資料前加上驗證程式,檢查是否有重複欄位。
  • 若必須保留重複資訊,請改用陣列或 key 包含唯一識別,例如: { "a_1": 1, "a_2": 2 }

更新成本高:整包改寫、效能會受影響

這是 JSONB 最常被忽略的一個陷阱。

雖然 JSONB 查詢效率很高,但它的更新(UPDATE)成本其實比想像中高。這是因為:

  • JSONB 是二進位格式,不支援「局部更新」。
  • 當你更新 JSONB 欄位中的某個值,即使只改了一個小屬性(例如把 age 改成 31),PostgreSQL 也會重新寫入整段 JSON 結構,而不是只改那個欄位。

對於資料結構簡單的小 JSON,這個成本可能可以接受。

但如果你有很大的 JSON 結構(像是產品資訊、使用者設定檔等),而且經常更新裡面的資料,這樣的重新寫入就會帶來以下問題:

  • 增加寫入負擔,尤其在高頻更新時。
  • 導致 WAL(Write Ahead Log)檔案膨脹,影響備份與還原。
  • 索引可能會失效或需要重建,拖慢效能。

建議做法

  • 避免將高頻更新的資料儲存在 JSONB 欄位。
  • 若某些欄位經常更新,考慮將它們拆出來獨立成為資料表欄位。
  • 若非必要,盡量不要對整個 JSONB 欄位做 UPDATE,而是用 jsonb_set() 函式進行局部更新(雖然實際上它底層還是會整體改寫,但語意清楚)。

什麼時候該用 JSONB?使用時機總整理

現在我們已經了解 JSONB 的優點與限制,接下來最常見的問題就是:「什麼情況下應該使用 JSONB?又什麼時候應該避免?」

以下是使用 JSONB 的適合情境

適合使用 JSONB 的狀況

  1. 資料結構多變或不確定
    • 例如使用者可以自訂資料欄位(像是客製欄位、自我介紹、社群帳號等)。
    • API 接收來的資料格式會隨不同來源而改變。
  2. 儲存彈性欄位
    • 比如你有一張「商品表」,每種商品有不同的屬性:手機有螢幕尺寸、相機畫素,衣服有尺寸、材質等。用 JSONB 可以統一存在一個欄位裡,不必每種商品都拆表。
  3. 希望能對 JSON 內容進行查詢、篩選或建立索引
    • 不像純文字欄位,JSONB 可搭配操作符號進行內容比對,還能建立 GIN 索引提高查詢效能。
  4. 結合關聯式與非結構化資料
    • 如果你的資料大部分是規則性的,但某些欄位需要彈性結構(例如客服紀錄、用戶行為分析原始資料),那 JSONB 是一個很好的輔助欄位。

不建議使用 JSONB 的狀況

  1. 資料結構固定且一致
    • 例如每筆資料都有「姓名、電話、地址」這類穩定欄位,就應使用傳統欄位(TEXT、INT 等)來設計表格,便於管理、效能更高。
  2. 資料更新頻繁而且內容大
    • 如即時交易資料、頻繁修改的設定檔,若使用 JSONB,整段資料每次都要改寫,容易造成效能瓶頸。
  3. 需要頻繁進行部分資料更新
    • 例如只要更新使用者的某個偏好設定,就改整段 JSONB 的話,不如把偏好設定獨立出來當成一般欄位或獨立表格。

結語:JSONB 是現代資料儲存的好幫手

對於初學者來說,理解 JSONB 可以幫助你更靈活地設計資料庫架構,特別是當資料結構不那麼固定時。

它結合了 NoSQL 的彈性與 SQL 的強大查詢能力,是現代開發中不可忽視的好工具。

記得,選擇工具時最重要的還是根據需求而定。學會 JSONB,只是你成為資料處理高手的第一步!

Similar Posts