初學者入門:什麼是 JSONB?
更新日期: 2025 年 3 月 24 日
在現代網路應用中,前端與後端之間常透過 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:差異比較
雖然 JSON
和 JSONB
都能儲存類似的資料,但它們在處理方式上其實有不少差異。以下用表格來說明兩者的主要不同:
特性 | JSON | JSONB |
---|---|---|
儲存格式 | 純文字(原樣儲存) | 二進位格式(解析後儲存) |
查詢效能 | 較慢,需即時解析字串 | 較快,因為預先轉換格式 |
可否建立索引 | 否 | 是(可使用 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 結構(包含
name
、age
、hobbies
)寫入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 的狀況
- 資料結構多變或不確定
- 例如使用者可以自訂資料欄位(像是客製欄位、自我介紹、社群帳號等)。
- API 接收來的資料格式會隨不同來源而改變。
- 儲存彈性欄位
- 比如你有一張「商品表」,每種商品有不同的屬性:手機有螢幕尺寸、相機畫素,衣服有尺寸、材質等。用 JSONB 可以統一存在一個欄位裡,不必每種商品都拆表。
- 希望能對 JSON 內容進行查詢、篩選或建立索引
- 不像純文字欄位,JSONB 可搭配操作符號進行內容比對,還能建立 GIN 索引提高查詢效能。
- 結合關聯式與非結構化資料
- 如果你的資料大部分是規則性的,但某些欄位需要彈性結構(例如客服紀錄、用戶行為分析原始資料),那 JSONB 是一個很好的輔助欄位。
不建議使用 JSONB 的狀況
- 資料結構固定且一致
- 例如每筆資料都有「姓名、電話、地址」這類穩定欄位,就應使用傳統欄位(TEXT、INT 等)來設計表格,便於管理、效能更高。
- 資料更新頻繁而且內容大
- 如即時交易資料、頻繁修改的設定檔,若使用 JSONB,整段資料每次都要改寫,容易造成效能瓶頸。
- 需要頻繁進行部分資料更新
- 例如只要更新使用者的某個偏好設定,就改整段 JSONB 的話,不如把偏好設定獨立出來當成一般欄位或獨立表格。
結語:JSONB 是現代資料儲存的好幫手
對於初學者來說,理解 JSONB 可以幫助你更靈活地設計資料庫架構,特別是當資料結構不那麼固定時。
它結合了 NoSQL 的彈性與 SQL 的強大查詢能力,是現代開發中不可忽視的好工具。
記得,選擇工具時最重要的還是根據需求而定。學會 JSONB,只是你成為資料處理高手的第一步!