13 年發現 pg 有了 json 類型,便從 oracle 轉 pg,幾年下來也算比較熟稔了,總結幾個有益的實踐。
用途一:存儲設計時無法預料的文檔性的數據。比如,通常可以在人員表准備一個 json 類型字段,名字叫 info、tag 之類。人員表是系統很難設計的表,常常需要擴充各類信息,如駕照號、社保號等等,在設計時不能全盤考慮到,這些信息的特點是用於登記、檢索,但與其它表沒有外鍵關系。有的信息有多個,比如教育經歷,包含有中學大學研究生等等。以往需要分出子表或以嵌套表存儲,實現復雜,造成 ER 圖日益龐大。引入 JSON 類型后,此類嵌套表幾乎都可以合並入 JSON 字段。
又如,網站系統的會員信息中需要保存APP用戶的設備信息。最開始可能設計為
| device |
| 9I6HHL7K3L4OU |
后來發現,需要保存設備的類型,JSON 字段可以演變為:
| device |
| {id:'9I6HHL7K3L4OU', type: 'ios'} |
繼而,用戶可能有多個設備需要記錄, JSON 字段可以追隨演變為:
| device |
| [{id:'9I6HHL7K3L4OU', type: 'ios'}, ...] |
可見,JSON 字段就像可以裝入大象的冰箱,打開了這道門就可以存儲無盡的信息。
采用 JSON 字段后,數據庫設計階段只需要考慮主要業務實體之間的結構,抓大放小,設計思路也更清晰而不至於瑣碎。我們知道,軟件系統是一個生長的過程,很多東西在設計初期都難以預料,考慮再周詳也有未盡之處,何況隨着系統使用,需求變更,原以為是屬性的,可能要改為數組,原以為是 flat 數組的,可能發現要改用對象數組,這些變遷屢見不鮮,如果采用靜態設計,Schema 需要反復調整,對於使用SSH 框架進行開發的,每次調整帶來的變動更多。JSON 類型使系統設計更能適合系統不斷的演變進化。
將嵌套表放在 JSON 字段中,也避免了 JOIN 帶來的性能損失。但有時需要從子表進行檢索,如需要查詢教育經歷包含本科的所有人,這種場景還是應當使用子表,或 JSON 信息和子表同時使用,子表僅用於此類需要從子表出發的檢索,這種數據冗余是值得的。
用途二:合並表間繼承。表間繼承關系通常采用“主從表同主鍵、從表主鍵外鍵到主表主鍵”實現,PG 數據庫還設計了專門的表繼承語法語義。引入 JSON 類型后,不少繼承可以取消。例如,之前常用的組織機構與公司、部門、科室為繼承關系,以往公司表、部門表、科室表都從組織機構表繼承。引入 JSON 后子類表可取消,合並到組織機構表,該表通過新增 TYPE 字段區分是何種類型的組織機構,對於不同類型的TYPE,其 INFO 字段(JSON) 擁有不同的結構。
這得益於 JSON 數據沒有固定結構,每個 JSON 對象都是自說明的,都可以擁有自己的結構,不像靜態語言的對象只能獲得從類型定義規定的結構。
用途三:從功能角度進行更高級別的抽象。
13 年編寫過一個 ETL 工具,在異構數據庫中間同步數據。該工具需要將源數據庫的數據采集到中心,再由中心分發到目標數據庫,數據要在中心 進行存儲。中心應該是中立於數據的,不應考慮傳輸的數據是服裝信息、葯品信息、還是人員信息,其只應具備傳輸保管能力,不應具備業務知識,如同 TCP 一樣,不應理解傳輸的是視頻還是 HTML,不能為傳輸HTML搞一個TCP,為傳輸視頻另搞一個TCP。
站在 ETL 功能的視角,所傳輸的內容都可以理解為對象,據此,可將采集到的數據行不識別結構的全部壓縮到一個名為 OBJECT 的 JSON 字段。另外在實際使用時還要考慮對象之間的主從依賴關系,在新增數據時,先新增主表行,再新增子表行,在刪除數據時反之。因此,還需要記憶所屬對象。最終這個轉儲中心數據表結構如下:
ID OBJECT(JSON) TYPE PARENT_ID(FK:ID)
該表其它外圍字段此處不贅述。
同樣,設計流程系統可着眼於工作環節,將工作環節的其它信息,如地點等等壓縮進 JSON 字段,根據這條原理我曾設計過一個萬能的 task – actor 表結構,將之前若干貌似各不相干的業務合並為前后串聯的 task。
activiti 如能用這種思路組織 task 等信息,當可達到更靈活便利的效果。
值得一提的是,d2js 框架高度支持 json 類型數據,數據庫的 json 取出即成為編程語言的對象,編程語言的對象也可直接存入數據庫,不需要借助 ORM 映射方式,為開發帶來了極大便利。
在沒有使用 json 前,很多人擔心 json 字段是一個整體無法檢索。這個擔心大可不必,json 字段存儲的對象也可對其屬性進行檢索,並可建立索引。當然,JSON字段也存在一點缺陷,作為一個整體,在更新時會一次性引發若干索引的更新。但相比得到的優勢來說,優勢更為明顯。
另外,在實踐中 pg 應使用 jsonb 字段類型,而不是 json 類型,jsonb 是 9.5 后推出的 binary 方式存儲的 json 類型,類似 mongodb 的 bson,而 json 類型是字符串方式存儲的,顯然 jsonb 更節約空間且效率更高。
pg 提供了不少極有用的 json 操作符和函數,開發者應一一試驗做一個接觸式了解。
PostgreSQL: Documentation: 9.6: JSON Functions and Operators
https://www.postgresql.org/docs/9.6/static/functions-json.html
