第 2 / 12 部 待錄

資料模型與查詢語言

《Designing Data-Intensive Applications(DDIA)》Podcast 準備稿:資料模型與查詢語言

書名: 設計資料密集型應用 Designing Data-Intensive Applications(The Big Ideas Behind Reliable, Scalable, and Maintainable Systems) 作者: Martin Kleppmann 系列: 啃一本大書(恩普拉氏) 公開標題建議: 啃一本大書|DDIA.關聯式、文件、圖——資料模型的三條路 (2/12) 涵蓋範圍: 第3章 資料模型與查詢語言(關聯式 vs 文件 vs 圖、宣告式 vs 命令式、SQL/MapReduce/Cypher/SPARQL/Datalog)

背景速覽

承接第一集的世界觀,這集進入 Part I 第一個技術主題:資料模型。Kleppmann 說資料模型可能是軟體開發中最重要的決策之一——因為它不只決定資料怎麼存,更決定你怎麼思考問題。這集走一趟資料模型的歷史與三大陣營(關聯式、文件、圖),並把「宣告式 vs 命令式」這條查詢語言的分水嶺講清楚。

一句話重點

沒有「最好的資料模型」,只有「最適合你資料形狀的模型」——一對多的樹狀資料適合文件、多對多的高度互聯資料適合圖、結構統一又常 JOIN 的資料適合關聯式;而比模型更深一層的洞見是:宣告式查詢(你說要什麼、不說怎麼做)幾乎總是贏過命令式,因為它把「怎麼做」的最佳化權力交還給資料庫。

值得討論的重點

1. 資料模型是層層堆疊的抽象——選錯一層,上面全部難受

從應用層的物件,到儲存層的 JSON/表格/圖,到引擎層的位元組,到硬體層的電流——每一層都靠「提供乾淨的模型」隱藏下層複雜度。重點是:這些模型對不同操作的支援差異極大。 選一個跟你的存取模式不合的模型,你會在上面寫一堆笨拙的轉譯程式碼來補。所以資料模型不是「先隨便選個資料庫」的小事,是影響整個應用形狀的大事。

2. 物件-關聯阻抗不匹配,與「文件模型的回歸」

現代應用多用物件導向語言,但關聯式資料庫是表格——這之間有個笨拙的轉譯層,叫阻抗不匹配(impedance mismatch),ORM 能減輕但消不掉。以 LinkedIn 個人檔案為例:一個人有一個名字、但有多筆工作經歷、學歷、聯絡方式(一對多)。關聯式要拆成好幾張表 JOIN;JSON 文件則把整棵樹塞在一份文件裡,資料局部性好、一次讀完。這就是文件模型的甜蜜點:自包含的一對多樹狀結構。

3. 歷史會重演嗎?文件模型 vs 1970 年代的階層式模型

這是本章最有縱深的一段。1970 年代 IBM 的 IMS 用階層式模型(巢狀樹),跟今天文件資料庫的 JSON 驚人地像——都很會處理一對多,但多對多很痛、不支援 JOIN。當年為了解決這個痛,分裂出兩條路:

  • 網路模型(CODASYL):一筆記錄可有多個父節點,靠手寫「存取路徑」導航——程式碼複雜、改模型代價高。
  • 關聯式模型:把資料攤平成表格,由查詢最佳化器自動決定存取路徑——寫一次最佳化器,所有應用受益。

關聯式贏了,靠的就是「把『怎麼存取』這件事自動化」。而今天文件資料庫在多對多上又遇到同樣的尷尬——歷史確實有點重演的味道。

4. 三種模型的選擇判準,一句話各自記住

  • 資料是自包含的一對多樹、整棵一起讀 → 文件模型好(局部性佳、schema 彈性)。
  • 應用有大量多對多關係 → 文件模型開始尷尬(JOIN 弱),關聯式尚可。
  • 資料高度互聯、什麼都可能連到什麼(社群、知識圖譜、道路網)→ 圖模型最自然。

而 schema 這一軸要記住兩個詞:文件式是 schema-on-read(讀時才解讀結構,像動態型別,適合異質資料);關聯式是 schema-on-write(寫時強制,像靜態型別,適合統一結構)。「無 schema」是個誤稱——其實是「隱式 schema」。

5. 宣告式 vs 命令式:這才是查詢語言真正的分水嶺

SQL 是宣告式(你描述要什麼結果,不說怎麼算),IMS/CODASYL 是命令式(你一步步指揮)。宣告式的三大優勢:更簡潔、隱藏實作細節(資料庫可在不改你查詢的前提下優化)、更適合平行化。Kleppmann 還拋了一個漂亮的類比:CSS(宣告式)之於 JavaScript 手動操作 DOM(命令式)——同一個道理。而 NoSQL 圈搞 MapReduce 搞到後來,MongoDB 又加回了宣告式的 aggregation pipeline——「NoSQL 不自覺地重新發明了 SQL」。

注意事項

⚠️ 別把「文件 vs 關聯式」講成宗教戰爭。 章末最重要的訊息是:兩者正在收斂。 Postgres/MySQL 早就支援 JSON 欄位與索引,文件資料庫(RethinkDB)也支援 JOIN。所以正確姿態不是「選邊站」,而是「看資料形狀選工具,甚至混用」(polyglot persistence)。提醒聽眾:2017 年之後這個收斂只有更明顯,今天的 Postgres 幾乎是「會做文件的關聯式資料庫」。

⚠️ 圖模型很強,但不要逢多對多就上圖資料庫。 圖模型在「高度互聯、要做多跳遍歷」時才真正發光(例如「找出所有從美國移居歐洲的人」這種遞迴查詢,Cypher 4 行、SQL 要 29 行的 WITH RECURSIVE)。但如果你的多對多其實有限、查詢也不深,關聯式的 JOIN 就夠了,多上一套圖資料庫是維運負擔。

⚠️ schema-on-read 不等於「沒有 schema、可以亂寫」。 資料仍然有結構,只是這個結構的責任從資料庫移到了應用程式。好處是改結構快(不用 migration),壞處是「壞資料」可能默默寫進去、讀的時候才爆。這個取捨要講清楚,免得聽眾以為文件資料庫是「自由的免費午餐」。

專家補充

💡 這章的真正主角其實是「查詢最佳化器」這個發明。 關聯式模型為什麼能贏 CODASYL?不是因為表格比樹漂亮,而是因為它把「怎麼存取資料」這件最難、最容易寫錯的事,從應用程式手上拿走,交給一個寫一次、人人受益的最佳化器。這是軟體工程「好抽象」的教科書級案例——呼應第一集講的「用抽象消滅偶發複雜度」。

💡 Datalog 值得一提,因為它是「未來感」的老東西。 Datalog 比 SPARQL、Cypher 還老(1980 年代),但它的「規則可組合、可遞迴、可重用」的思路,在現代資料系統(Datomic、Cascalog)裡重新被重視。它逼你用不同的方式思考——不是「我要這筆資料」,而是「我定義一組推導規則」。對複雜資料,這種方式表達力驚人。

💡 「資料局部性」這條線會貫穿整本書。 文件模型一次讀完一棵樹,靠的就是局部性;但 Kleppmann 提醒:局部性的好處只在你需要「大部分文件」時成立,而且更新時通常要重寫整份文件,所以文件要保持精簡。這個「把相關資料放在一起以減少 I/O」的思想,下一集講儲存引擎(列式儲存)、講 Spanner 的 interleaved tables 時還會再出現。

討論問題

🎙️ 開場鉤子:「你有沒有想過,為什麼有些系統用 MongoDB 一開始爽到飛起、後來卻越來越難改?答案藏在 1970 年代一個叫 IMS 的老資料庫裡——今天的問題,當年早就上演過一次了。」

🎙️ 自問自答:「SQL 寫起來明明很『不直覺』,為什麼它能統治業界半個世紀?」用宣告式 vs 命令式回答:因為你只說『要什麼』、不說『怎麼做』,於是資料庫可以偷偷把查詢優化一百倍,而你一行 code 都不用改。

🎙️ 帶走的一題:「想一下你手上專案的核心資料——它是『一棵棵獨立的樹』,還是『一張什麼都連到什麼的網』?這個形狀,其實已經幫你選好了資料模型。」

更大範圍關聯

  • 與 Alex Xu 系統設計面試的分工:面試題常問「選 SQL 還是 NoSQL」,但面試答案往往停在「NoSQL 比較好 scale」這種口號。DDIA 這一章給你的是判準的底層邏輯——資料形狀、存取模式、schema 責任歸屬。有了這層,你在面試裡才答得出「為什麼」,而不只是「聽說」。(見 [軟體工程領域切分藍圖] §5「軟體架構演進」)
  • 思想史座標:關聯式 vs 階層式/網路式的這場 1970 年代論戰,是資料庫史上的「第一次正規化革命」;NoSQL 是「第二次反動」;而今天的收斂是「第三次和解」。DDIA 站在和解點上回望,所以講得特別清醒。
  • 接下一集:這集談「資料用什麼模型表示」,下一集(儲存與檢索)往下鑽一層——這些模型在磁碟上到底怎麼存、怎麼建索引。LSM-tree 與 B-tree 的世紀對決,就在下集。

錄製建議

  • 建議時長:約 20–22 分鐘。配比:模型三陣營與選擇判準約 10 分(主菜);「歷史重演」的 IMS 對照約 4 分(最有縱深,慢慢講);宣告式 vs 命令式約 6 分(用 CSS 類比讓它好懂)。
  • 討論策略:用「MongoDB 先爽後痛」的真實體感開場,帶出多對多的尷尬;中段把三種模型的選擇判準各給一個一句話標籤,方便聽眾複述;結尾把宣告式的價值接回第一集的「好抽象」,整季的主線就串起來了。
  • 圖查詢的 Cypher 4 行 vs SQL 29 行,可以口頭把那個對比講出來(不用唸 code),聽眾會很有感——「差了七倍的程式碼量,這就是選對模型的威力」。