PostgreSQL JSON 與 JSONB 基本區別

JSON 和 JSONB 是 PostgreSQL 中兩種不同的 JSON 數據類型。JSON 存儲的是原始 JSON 文本,保留格式(如空格、鍵順序),查詢時需要實時解析。JSONB 以二進制格式存儲,解析後的數據會被重新編排,犧牲部分插入性能以換取更高的查詢效率。

大數據量下的寫入性能差異

JSON 類型在插入時僅做簡單的語法校驗,寫入速度更快。JSONB 需要額外處理為二進制格式,寫入時會有 10%-20% 的性能損耗。對於超高頻寫入場景(如日誌存儲),JSON 可能更合適。

-- JSON插入示例(更快)
INSERT INTO logs (data) VALUES ('{"event": "login", "time": "2023-01-01"}');

-- JSONB插入示例(稍慢)
INSERT INTO logs (data_b) VALUES ('{"event": "login", "time": "2023-01-01"}'::jsonb);

查詢性能對比

JSONB 在幾乎所有查詢場景下都顯著優於 JSON。測試顯示,在 100 萬條記錄中查詢特定鍵值時:

  • JSONB 的查詢速度比 JSON 快 5-10 倍
  • JSONB 支持 GIN 索引,對複雜查詢可提升 100 倍以上性能
-- 創建GIN索引(僅JSONB支持)
CREATE INDEX idx_data_b ON logs USING gin (data_b);

-- 查詢性能對比
EXPLAIN ANALYZE SELECT * FROM logs WHERE data->>'event' = 'login';  -- JSON
EXPLAIN ANALYZE SELECT * FROM logs WHERE data_b->>'event' = 'login'; -- JSONB

存儲空間佔用

JSONB 通常會比 JSON 多佔用 2-5% 的存儲空間,因其存儲了額外的元數據。但在壓縮場景下(如 TOAST 存儲),兩者的差距會縮小。

特定操作性能差異

  • 鍵存在檢查:JSONB 使用 ? 操作符比 JSON 的 exists() 函數快 8-10 倍
  • 路徑查詢:JSONB 的 @> 操作符比 JSON 的路徑操作快 15-20 倍
  • 更新操作:JSONB 支持部分更新(9.5+版本),比 JSON 的整列替換更高效
-- 鍵存在檢查對比
SELECT * FROM logs WHERE data_b ? 'event';  -- JSONB(更快)
SELECT * FROM logs WHERE exist(data::json, 'event');  -- JSON

-- 路徑查詢對比
SELECT * FROM logs WHERE data_b @> '{"event": "login"}';  -- JSONB

大數據場景建議

  1. 讀多寫少:優先選擇 JSONB,利用其索引優勢
  2. 高頻寫入:若僅需追加存儲且很少查詢,考慮 JSON
  3. 混合負載:使用 JSONB 並考慮分區表或讀寫分離
  4. 版本選擇:PostgreSQL 12+ 對 JSONB 有額外優化,建議使用新版本

性能優化策略

  • 對常用查詢字段創建單獨的 GIN 索引
  • 對 JSONB 使用部分更新替代整列替換
  • 考慮使用 jsonb_path_ops 索引優化特定路徑查詢
  • 對大文檔使用壓縮存儲(TOAST)
-- 創建優化後的GIN索引
CREATE INDEX idx_data_b_path ON logs USING gin (data_b jsonb_path_ops);

-- 部分更新示例(僅更新特定字段)
UPDATE logs SET data_b = jsonb_set(data_b, '{time}', '"2023-01-02"') WHERE id = 1;