Relational Model and Document Model

本文為 Design Data Intensive Applications 的書摘 + 個人心得。

資料模型 (Data Model) 是所有軟體開發中最重要的環節,每個資料表示層級要如何向更低層級表達資料項目?

看起來有點饒舌,資料表示層級舉例來看:

  1. 應用程式端 (Application) 諸如金流、物流、訂單、貨物資料等等,這些項目都是表達了現實世界中你的應用程式會使用到的資料項目。
  2. 資料模型端 (Data Model) 當你決定了資料項目後,在來就要想用什麼 data model 來表達,例如 JSON、XML document、relation database 或者 graph model。
  3. 儲存與檢索端 (Storage and Retrieval) 該用哪個 database 儲存,每種 database 都有多種查詢、操作資料的方法,如 Hash Index、B-Tress 等等,這部份在 Day 6 會講到。
  4. 硬體端 (Hardware) 硬體工程師的世界,通常我們只決定要用多少系統資源執行。

每一層都是在向下一層隱藏該層的複雜性,提供給他們乾淨的 Data Model,今天的主題就是講 Data Model 中的 Relation Model 跟 Document Model,就是常聽到的 SQL 跟 NoSQL。

SQL – Rleational Model

關聯式資料模型 (Relational Model) 加上 SQL 跟 RDBMS 的推出,這主宰了軟體開發領域長達 25-30 年之久,

Relational model 最早 Edgar Codd 在 1970 年提出的學術文章:資料是組織在關係裡面的 (可視為 table in SQL),每一個關係是未經排序的 tuple 集合 (可視為 row in SQL)。

然後在 1980 年中,relational database management systems (RDBMS) 和 SQL 成為一套標準化的工具,讓人們能用特定結構性的語法去查詢資料。

NoSQL – Document model

NoSQL 一詞不代表特定的任一技術,它代表了分散式、非關聯式資料,開始有革命感覺是來自 2009 年在亞特蘭大舉行的會議 ,NoSQL 不是反對 SQL 之意,比較精準的詞應是 Not Only SQL

NoSQL 有下述幾項特點

  • 一個需要比 Relational Database 有更棒擴充能力且易於執行。
  • 有特定的查詢語言且不太支援 Realtional Model。
  • 對 Realtional Database 的 schema 限制感到失望,渴望能更動態調整 Data Model 。

Relational Model 和 Document Model

figure_2-1

上圖是 Bill Gates 的 LinkedIn Profile 用 reational schema 的方式來表達,這張圖的重點是我們用 Relational Model 來表達一個人可能會有多個工作經歷、教育背景以及聯絡方式,就是 position、education、contact_info 這 3 個 table,這 3 個 table 都存了 user_id 這個 foreign key 這個參照來找到 user 是誰。

像 Profile 這種資料也常用 Document Model 的方式來表達,以 JSON 來呈現的話長這樣:

{
  "user_id": 251,
  "first_name": "Bill",
  "last_name": "Gates",
  "summary": "Co-chair of the Bill & Melinda Gates... Active blogger.",
  "region_id": "us:91",
  "industry_id": 131,
  "photo_url": "/p/7/000/253/05b/308dd6e.jpg",
  "positions": [
    {
      "job_title": "Co-chair",
      "organization": "Bill & Melinda Gates Foundation"
    },
    {
      "job_title": "Co-founder, Chairman",
      "organization": "Microsoft"
    }
  ],
  "education": [
    {
      "school_name": "Harvard University",
      "start": 1973,
      "end": 1975
    },
    {
      "school_name": "Lakeside School, Seattle",
      "start": null,
      "end": null
    }
  ],
  "contact_info": {
    "blog": "http://thegatesnotes.com",
    "twitter": "http://twitter.com/BillGates"
  }
}

最大的差別就是 Self-Contained,工作經歷、教育、聯絡方式都包含在整個 LinkedIn Profile 之中,用 XML 呈現也是一樣意思,但 JSON 比 XML 簡單和好理解太多了XD

Relations (資料關係)

one to many

一對多的闗係就是上面提到的,一個人可能會有多個工作經歷、教育背景以及聯絡方式,把這關係轉成 Tree 結構的話就更好理解了。

figure_2-2

many to one

多對一的關係,繼續用 Bill Gates 的 LinkedIn Profile 來看,我們的居住地區跟職務都是用 ID 來表達,而不是直接寫 “Greater Seattle Area” 和 “Philanthropy”,為什麼呢?

"region_id": "us:91",
"industry_id": 131,

最主要的原因就是為了 標準化 (Normalization),維護一個表的話代表居住地或職務一樣的人都會有相同的資料,修改方便也能統一格式,找資料也更快,雖然 ID 只對資料庫有意義就是了,但 去重複 就是 Relational Database 中標準化的關鍵想法。

標準化 (normalization) 的反面就是 去標準化 (Denormalization),也就是在 Document Database 裡常用到的,不會用額外的 table 儲存資料,每個 LinkedIn Profile 的居住地區跟職務都會存成:

{
  "region_id": {
    "conutry": "US",
    "area": "Greater Seattle Area"
  },
  "industry_id": "Philanthropy"
}

many to many

依舊繼續用 Bill Gates 的 LinkedIn Profile 來看,若我們想讓 Profile 加上 2 個新功能:

  • 顯示 Microsoft 公司和學校的基本資料,如下圖:
  • user 可以為另一個 user 寫推薦,推薦裡可以看到推薦者 user 的 Profile,若推薦者的 Profile 有資料變更,被推薦者看到的 推薦者 Profile 內容也需要更新,所以被推薦者的 Profile 應該要存推薦者的 user 參照。

下圖是加上 2 個新功能後所需要的多對多關係示意圖:

figure_2-4

虛線可被存成 Document Model,在這種情況下,公司、學校、推薦者都需要儲存參照,然後做多次查詢是變的必要了。

Relational vs. Document database

這裡有許多可以比較的點,像容錯能力和如何處理並行執行緒,但最主要的差異點就是 document model 的 schema 彈性較高,且 local 環境下執行效率也比較好,relational morel 則是比較支援 JOIN 及多對一、多對多關係。

誰讓程式變的比較簡單?

如果你的軟體用的資料項目是符合 Tree 那樣的一對多關係,Document Model 會比較簡單,最大的差異點就是不用像 relational model 那樣分成多個 table,也代表不用理會每個 table 各別的 schema 是如何,也降低應用端的程式複雜度。

但如果你的軟體用資料項目是傾向多對多關係,Document Model 可能就不是首選了,有鑑於 document model 對 join 的支援很貧乏,減少 JOIN 的可能方法之一就是做 denormalization (去標準化),或者查詢多次,但如此就會提高應用端的程式複雜度,JOIN 在這就是比較好的選擇了,把多次查詢合成一次,這就是把複雜度移往 database 端的例子。

很難說誰讓程式變的簡單了,要基於應用端資料項目的關係為何,有時使用 graph data model 還可能讓程式更簡單。

Document Model 的 Schema 彈性

Document database 的 schema 是只有在你讀取資料並需要對它做解釋時才會用到,稱做 schema-on-read,可以把它視為一種隱含的 schema,相反的 Relational database 的 schema 就稱做 schema-on-write,這就是明確的 schema 定義了,在你寫入資料時會確保資料符合 schema 所定義的格式。

schema-on-read 很像動態程式語言 (執行時檢查),像 Python、PHP,schema-on-write 則是像靜態程式語言 (編譯時檢查),像 Java、C,你無法斷定說哪種類型的程式語言較棒,同理也是得如此這樣看 data model 的 schema 處理方式。

但是 NoSQL 會紅也不是沒有道理的,schema-on-read 在某種程度上帶來非常大的彈性,舉例來說,目前你的 user table 裡,完整的 name 是儲存在一個欄位裡,然後你需要把 name 切成 first_namelast_name 2 個欄位儲存,在 Document database 裡,你可以直接在新資料儲存裡加上這 2 個欄位,然後只要新增下面這段 code,舊的資料在讀取時也就有這 2 個欄位了。

if (user && user.name && !user.first_name) {
    // Documents written before Dec 8, 2013 don't have first_name
    user.first_name = user.name.split(" ")[0];
}

但在 Realtional database 該怎麼做呢?你得要變更 data schema 新增欄位然後 update 舊資料,

ALTER TABLE users ADD COLUMN first_name text;
UPDATE users SET first_name = split_part(name, ' ', 1); -- PostgreSQL 
UPDATE users SET first_name = substring_index(name, ' ', 1); -- MySQL

不用說 alter table 是一種很昂貴的操作,database 可能會變慢也有可能短期內無法提供服務,update 操作亦同。

schema-on-read 的一大優點就是你不用保持所有的資料都是同一種資料架構,例如你的資料來源是外部系統,你無法控制對方什麼時候改變資料型態,在這情況下,Relational database 的 schema 造成的傷害會比幫助多,之後會談到如何做到 document database 的 schema 進化 (evolution)。

查詢時的資料區域性 (Data locality for queries)

documnet 在儲存時通常會存成連續的字串,像 JSON、XML 或變體的 binary 字串 (MongoDB 的 BSON),如果你的軟體是需要一整個 documnet 做事情的,例如產生網頁,這時 storage locality (儲存區域性) 就有優勢了,不用像圖 2-1 那樣,多個 table 代表會有更多的 index 查找跟更多的查詢時間。

這裡值得提出的一點是把相關的資料為了 locality 原因而 group 起來並不侷限在 Documnet Model 而已,像 BigTable 的資料模型 column-family 就是類似概念 (用在 HBase 和 Cassandra),未來會提到更多。

逐漸互相靠近的 relational 與 document database

現在 MySQL 和 PostgreSQL 都開始支援 JSON 和 XML 這種 documnet 類型的資料型態,在 document database 裡,RethinkDB 在 query 中支援了 relational-join , MongoDB 則是用 lookup 的方式做到 join 的情景,但一定比 realtional database 來的慢。

未來 database 一定是往混合的道路走,工程師就有更好的工具完成資料模型的設計了。

tshine73
tshine73
文章: 50

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *