【分佈式—基礎】數據模型與查詢語言

大多數應用程序是經過一層一層疊加數據模型來構建的。每一層都面臨的關鍵問題是:如何將其用下一層來表示?例如:數據庫

  1. 做爲一名應用程序開發人員,觀測現實世界(其中包括人員、組織、貨物、行爲、資金流動、傳感器等),經過對象或數據結構,以及操做這些數據結構的API來對其建模。這些數據結構每每特定於該應用。
  2. 當須要存儲這些數據結構時,能夠採用通用數據模型(例如JSON或XML文檔、關係數據庫中的表或圖模型)來表示。
  3. 數據庫工程師接着決定用何種內存、磁盤或網絡的字節格式來表示上述JSON/XML/關係/圖形數據。數據表示須要支持多種方式的查詢、搜索、操做和處理數據。
  4. 在更下一層,硬件工程師則須要考慮用電流、光脈衝、磁場等來表示字節。

複雜的應用程序可能會有更多的中間層,例如基於API來構建上層API,可是基本思想相同:每層都經過提供一個簡潔的數據模型來隱藏下層的複雜性。有許多不一樣類型的數據模型,每種數據模型都有其最佳使用的若干假設。編程

關係模型與文檔模型

如今最著名的數據模型多是SQL,它基於Edgar Codd於1970年提出的關係模型:數據被組織成關係(relations),在SQL中稱爲表(table),其中每一個關係都是元組(tuples)的無序集合(在SQL中稱爲行)segmentfault

NoSQL

採用NoSQL數據庫有這樣幾個驅動因素,包括:網絡

  • 比關係數據庫更好的擴展性需求,包括支持超大數據集或超高寫入吞吐量。
  • NoSQL廣泛是免費和開源軟件。
  • 關係模型不能很好地支持一些特定的查詢操做
  • 關係模型具備一些限制性,NoSQL是更具動態和表達力的數據模型。

對象—關係不匹配

如今大多數應用開發都採用面向對象的編程語言,因爲兼容性問題,廣泛對SQL數據模型存在抱怨:若是數據存儲在關係表中,那麼應用層代碼中的對象與表、行和列的數據庫模型之間須要一個笨拙的轉換層。數據結構

例如,如何在關係模式中表示簡歷。整個簡歷能夠經過惟一的標識符user_id來標識。像first_name和last_name這樣的字段在每一個用戶中只出現一次,因此能夠將其建模爲users表中的列。然而,大多數人在他們的職業中有一個以上的工做,而且可能有多個教育階段和任意數量的聯繫信息。用戶與這些項目之間存在一對多的關係,能夠用多種方式來表示:併發

  • 在傳統的SQL模型(SQL: 1999以前)中,最多見的規範化表示是將職位、教育和聯繫信息放在單獨的表中,並使用外鍵引用users表,以下圖所示。
  • 以後的SQL標準增長了對結構化數據類型和XML數據的支持。這容許將多值數據存儲在單行內,並支持在這些文檔中查詢和索引。Oracle、IBM DB二、MSSQL Server和PostgreSQL都不一樣程度上支持這些功能。一些數據庫也支持JSON數據類型,例如IBM DB二、MySQL和PostgreSQL。
  • 第三個選項是將工做、教育和聯繫信息編碼爲JSON或XML文檔,將其存儲在數據庫的文本列中,並由應用程序解釋其結構和內容。對於此方法,一般不能使用數據庫查詢該編碼列中的值。

image.png

對於像簡歷這樣的數據結構,它主要是一個自包含的文檔(document),所以用JSON表示很是合適,參見以下示例。與XML相比,JSON的吸引力在於它更簡單。面向文檔的數據庫(如MongoDB、 RethinkDB、CouchDB和Espresso)都支持該數據模型。編程語言

{
    "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 8 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: /lthegatesnotes.com",
        "twitter" :"http: //twitter.com/BillGates"
    }
}

多對一與多對多的關係

在上面的示例中,region_id和industry_id定義爲ID,而不是純文本字符串形式,例如"Greater Seattle Area"和"Philanthropy"。爲何這樣作呢?ide

若是用戶界面是能夠輸入地區或行業的自由文本字段,則將其存儲爲純文本字符串更有意義。可是,擁有地理區域和行業的標準化列表,並讓用戶從下拉列表或自動填充器中進行選擇會更有優點,這樣:性能

  • 全部的簡歷保持樣式和輸入值一致
  • 避免歧義(例如,若是存在一些同名的城市)。
  • 易於更新:名字只保存一次,所以,若是須要改變(例如,因爲政治事件而更改城市名稱),能夠很容易全面更新。
  • 本地化支持:當網站被翻譯成其餘語言時,標準化的列表能夠方便本地化,所以地區和行業能夠用查看者的母語來顯示。
  • 更好的搜索支持:例如,搜索華盛頓州的慈善家能夠匹配到這個簡歷,這是由於地區列表能夠將西雅圖屬於華盛頓的信息編碼進來(而從「大西雅圖地區」字符串中並不能看出來西雅圖屬於華盛頓)。

使用ID的好處是,由於它對人類沒有任何直接意義,因此永遠不須要直接改變:即便ID標識的信息發生了變化,它也能夠保持不變。任何對人類有意義的東西均可能在未來某個時刻發生變動。若是這些信息被複制,那麼全部的冗餘副本也都須要更新。這會致使更多寫人開銷,而且存在數據不一致的風險(信息的一些副本被更新,而其餘副本未更新)。消除這種重複正是數據庫規範化的核心思想。大數據

然而這種數據規範化須要表達多對一的關係(許多人生活在同一地區,許多人在同一行業工做),這並非很適合文檔模型。

文檔數據庫是某種方式的層次模型:即在其父記錄中保存了嵌套記錄(一對多關係,如前面例子中的positions、education和contact_info),而不是存儲在單獨的表中。

可是,在表示多對一和多對多的關係時,關係數據庫和文檔數據庫並無根本的不一樣:在這兩種狀況下,相關項都由惟一的標識符引用,該標識符在關係模型中被稱爲外鍵,在文檔模型中被稱爲文檔引用。標識符能夠查詢時經過聯結操做或相關後續查詢來解析。

對於關係數據庫,因爲支持聯結操做,能夠很方便地經過ID來引用其餘表中的行。而在文檔數據庫中,一對多的樹狀結構不須要聯結,支持聯結一般也很弱。

即便應用程序的初始版本很是適合採用無聯結的文檔模型,但隨着應用支持愈來愈多的功能,數據也變得更加互聯一體化。

關係數據庫與文檔數據庫現狀

在比較關係數據庫與文檔數據庫時,須要考慮不少方面的差別,包括它們的容錯性和併發處理,這裏只關注數據模型中的差別。

支持文檔數據模型的主要論點是模式靈活性,因爲局部性而帶來較好的性能,對於某些應用來講,它更接近於應用程序所使用的數據結構。關係模型則強在聯結操做、多對一和多對多關係更簡潔的表達上,與文檔模型抗衡。

哪一種數據模型的應用代碼更簡單?

若是應用數據具備相似文檔的結構(即一對多關係樹,一般一次加載整個樹),那麼使用文檔模型更爲合適。而關係型模型則傾向於某種數據分解,它把文檔結構分解爲多個表,有可能使得模式更爲笨重,以及沒必要要的應用代碼複雜化。

文檔模型也有必定的侷限性:例如,不能直接引用文檔中的嵌套項,而須要說「用戶251的職位列表中的第二項」(很是相似於層次模型中的訪問路徑)。然而,只要文檔嵌套不太深,這一般不是問題。

在文檔數據庫中,對聯結的支持不足是不是問題取決於應用程序。例如,在使用文檔數據庫記錄事件發生時間的應用分析程序中,可能永遠不須要多對多關係。

可是,若是應用程序確實使用了多對多關係,那麼文檔模型就變得不太吸引人。能夠經過反規範化來減小對聯結的需求,可是應用程序代碼須要作額外的工做來保持非規範化數據的一致性。經過向數據庫發出多個請求,能夠在應用程序代碼中模擬聯結,可是這也將應用程序變得複雜,而且一般比數據庫內的專用代碼執行的聯結慢。在這些狀況下,使用文檔模型會致使應用程序代碼更復雜、性能更差。

一般沒法一律而論哪一種數據模型的應用代碼更簡單。這主要取決於數據項之間的關係類型。對於高度關聯的數據,文檔模型不太適合,關係模型能夠勝任,而圖模型則是最爲天然的。

文檔模型中的模式靈活性

大多數文檔數據庫,以及關係數據庫中的JSON支持,都不會對文檔中的數據強制執行任何模式驗證。關係數據庫中的XML一般支持帶有可選的模式驗證功能。沒有模式驗證意味着能夠將任意的鍵-值添加到文檔中,而且在讀取時,客戶端沒法保證文檔可能包含哪些字段。

文檔數據庫有時被稱爲無模式,但這具備誤導性,由於讀數據的代碼一般採用某種結構於是存在某種隱形模式,而不是由數據庫強制執行。更準確的術語應該是讀時模式(數據的結構是隱式的,只有在讀取時才解釋),與寫時模式(關係數據庫的一種傳統方法,模式是顯式的,而且數據庫確保數據寫入時都必須遵循)相對應。

讀時模式相似編程語言中的動態(運行時)類型檢查,而寫時模式相似於靜態(編譯時)類型檢查。正如靜態與動態類型檢查的支持者對於它們的優缺點存在很大的爭議同樣,數據庫的模式執行也是一個有爭議的話題,一般沒有明確正確或錯誤的答案。

若是集合中的項因爲某種緣由(例如數據異構),並不都具備相同的結構,例如:

  • 有許多不一樣類型的對象,將每種類型的對象都保存在各自的表中不太現實。
  • 數據的結構由沒法控制的外部系統所決定,並且可能隨時改變。

在這些狀況下,模式帶來的損害大於它所能提供的幫助,無模式文檔多是更天然的數據模型。可是,當全部記錄都有相同結構時,模式則是記錄和確保這種結構的有效機制。

查詢的數據局部性

文檔一般存儲爲編碼爲JSON、XML或其二進制變體(如MongoDB的BSON)的連續字符串。若是應用程序須要頻繁訪問整個文檔,則存儲局部性具備性能優點。若是數據被劃分在多個表中(關係模型),則須要進行屢次索引查找來檢索全部數據,中間可能須要更多的磁盤I/O並花費更多的時間。

局部性優點僅適用須要同時訪問文檔大部份內容的場景。因爲數據庫一般會加載整個文檔,若是應用只是訪問其中的一小部分,則對於大型文檔數據來說就有些浪費。對文檔進行更新時,一般會重寫整個文檔,而只有修改量不改變源文檔大小時,原地覆蓋更新才更有效。所以,一般建議文檔應該儘可能小且避免寫人時增長文檔大小。這些性能方面的不利因素大大限制了文檔數據庫的適用場景。

文檔數據庫與關係數據庫的融合

隨着時間的推移,彷佛關係數據庫與文檔數據庫變得愈來愈相近,或許這是一件好事:數據模型能夠相互補充。若是數據庫可以很好處理文檔類數據,還能對其執行關係查詢,那麼應用程序可使用最符合其需求的功能的組合。

融合關係模型與文檔模型是將來數據庫發展的一條很好的途徑。

數據查詢語言

當關系模型是最初被引入時,就包含了查詢數據的新方法:SQL是一種聲明式查詢語言,而IMS和CODASYL則是命令式。這種差異意味着什麼呢?

命令式語言告訴計算機以特定順序執行某些操做。你徹底能夠推理整個過程,逐行遍歷代碼、評估相關條件、更新對應的變量,並決定是否再循環一遍。

而對於聲明式查詢語言(如SQL或關係代數),則只需指定所需的數據模式,結果需知足什麼條件,以及如何轉換數據(例如,排序、分組和聚合),而不需指明如何實現這一目標。數據庫系統的查詢優化器會決定採用哪些索引和聯結,以及用何種順序來執行查詢的各個語句。

相關文章
相關標籤/搜索