jsonb 能比資料模型,不能比擴展架構
「PostgreSQL 有 jsonb,那還需要 NoSQL 嗎?」——可以比,但只能比一半。
離開關聯式資料庫通常有兩個理由,它們其實是獨立的:
| 理由 | jsonb 解決嗎 |
|---|---|
| (a) 資料沒有固定格式(欄位會變、巢狀、每筆不一樣) | ✅ 完全解決 |
| (b) 極高資料量,單機撐不住,需要水平擴展 | ❌ 完全沒幫助 |
jsonb 對應的是 NoSQL 的「資料模型」那一半,不對應「分散式架構」那一半。 MongoDB 的 sharding、DynamoDB 的 partitioning 是營運層的能力,跟「資料長得像文件」無關。
想用 NoSQL 的理由是 schema 會變 → 用 jsonb,不要換資料庫。 理由是 一台機器裝不下/撐不住 → jsonb 一點忙都幫不上。
jsonb 贏在一致性,不是效能
跟「兩套資料庫交錯使用」相比:
CREATE TABLE documents (
id bigserial PRIMARY KEY,
owner_id bigint REFERENCES users(id), -- 關聯、外鍵、約束照舊
updated_at timestamptz NOT NULL,
content jsonb NOT NULL -- 這格愛怎麼變怎麼變
);同一列、同一筆交易裡,結構化的部分享有外鍵與 ACID,變動的部分享有 schema-less。跨兩套資料庫寫入時,你得自己處理「SQL 寫成功但 NoSQL 失敗」——那個成本通常沒有被算進去。
實務上會踩到的代價
| 代價 | 說明 |
|---|---|
| MVCC 寫放大 | 改 jsonb 裡一個小欄位,整份文件會被重寫一次(新列版本)。文件大又改得頻繁 → bloat 與 vacuum 壓力 |
| TOAST | 超過約 2KB 會被壓縮並搬到行外儲存,讀取多一跳 |
| 索引要自己想 | GIN 能查 containment(@>)但體積大、寫入慢;固定路徑改建表達式索引 |
CREATE INDEX ON documents ((content->>'status'));這裡有個陷阱:索引必須跟 WHERE 裡的表達式長得一模一樣,否則用不到——見 把欄位包進函數,WHERE 就用不到那個欄位的索引。
預設值
先用 jsonb,等你能明確講出「是上面哪一條讓我非換不可」再換。 這跟 反正規化要排在索引與物化視圖後面 的精神一致——換資料庫是梯子的最後一階,不是第一階。
型別本身的細節見 什麼是 jsonb。