Object-Relational VS NoSQL
翻譯《Designing Data-Intensive Applications》
作者:Martin Kleppmann
譯者:雨釣(有增改)
一、SQL與NOSQL起源與優劣對比
1.1、SQL
今天最著名的資料結構可能就是SQL了,一種基於Edgar Codd在1970年提出的關係模型: 資料被組織成關係(SQL中的表),其中每個關係是一個無序的元組集合(SQL中的行), 關係模型是一個理論上的建議,許多人當時懷疑它是否能有效地實現。 然而,到20世紀80年代中期,關係資料庫管理系統(RDBMSes)和SQL已經成為大多數人的首選工具,他們需要儲存和查詢帶有某種regular結構的資料。 關係資料庫的主導地位持續了大約25-30年——這在計算曆史上是絕無僅有的;
關係資料庫的根源在於業務資料處理,主要是執行在上世紀六、七十年代的一種大型計算機上。它所針對的用例從今天的角度來看是很平常的,例如:典型的交易處理(銷售或銀行中轉業務,航空公司預訂,倉庫庫存)和批處理( 使用者的發票、工資、報告)。哪些當時與關係型資料庫共存的其他資料庫,由於其本身設計的問題,使得應用程式開發人員需要對資料庫中資料的內部結構進行大量的思考和優化。而與之相反,關係模型的目標是將實現細節隱藏在一個更乾淨的介面後面。
多年來,針對於資料儲存和查詢的方法有很多。 20世紀70年代和80年代初,網路模型和層次模型是當時主要的選擇。 但是伴隨著關係模式的出現和快速發展,關係模型主鍵佔據主導地位。 在上世紀80年代末和90年代初,Object資料庫再次出現, XML資料庫出現於本世紀初,但只出現了小眾的採用。 關係模型的每個競爭對手在這段時間內都進行了大量的宣傳和炒作但是仍然未能生存下來。
隨著計算機變得更加強大和網路化,它們開始被用於越來越多樣化的場景。值得注意的是,關係資料庫在其原始的業務資料處理範圍之外,非常好地推廣到針對WEB的應用上。 你在web上看到的許多東西仍然是由關係資料庫驅動的,比如線上釋出、討論、社交網路、電子商務、遊戲、軟體服務等等。
1.2、The Birth of No SQL
2010年 出現的NOSQL是推翻關係模型統治地位的一次最新嘗試。“No SQL”這個名字是不準確的,因為它實際上並不是指任何特定的技術——它最初只是一個簡單的Twitter在2009年提出的一個標籤,主要關於分散式,非關係資料庫,只後很快就傳遍了業界。現在,許多有趣的資料庫系統都與No SQL相關,並且它被重新定義為 NOT Only SQL 。
在NO SQL資料庫的情況下,有幾個驅動因素,包括:
-
比關係型資料庫更好的擴充套件性,包括更大的資料集以及吞吐量。
-
更好的開源特性而不是商業化的。
-
擁有關係模型所不支援的特定查詢操作
-
拋棄了關係模型中對schema的限制,支援更動態和更具代表性的資料模型
不同的應用程式有不同的需求,對於一個用例場景來說,最好的技術選擇可能與另一個用例的最佳選擇是不同。因此,在可預見的將來,關係資料庫將繼續與廣泛的非關係資料儲存共存,這一概念有時被稱為 polyglot persistence
二、The Object-Relational Mismatch
當今大多數應用程式開發都是在面向物件的程式語言中完成的,這導致了開發人員對SQL資料模型存在很多意見,因為如果資料儲存在關係表中,那麼在應用程式碼中的物件(面向物件的語言,如JAVA)和關係型模型中的表之間需要一個笨拙的轉換層。 模型之間的討論有時被稱為 impedance mismatch ( 從電子產品中借用的術語。每個電路的輸入和輸出都有一定的阻抗。當你將一個電路的輸出連線到另一個電路的輸入時,如果兩個電路的輸出和輸入阻抗匹配,連線上的power transfer就會最大化。阻抗不匹配會導致訊號反射和其他問題)
因此則常見的面向物件的開發中,如JAVA Web專案中通常會使用如Active Record和Hibernate等 物件-關係對映(ORM)框架 ,以減少這個翻譯層所需的樣板程式碼量。但它們並不能完全隱藏這兩個模型之間的差異。

圖2-1
例如,上圖說明了在關係模式中如何表示一份簡歷(概要檔案中的連結)。整個檔案可以通過一個唯一一的識別符號user_id來標識, 像first_name和last_name這樣的欄位恰好顯示為每一個使用者,因此它們可以被建模為users表上的列。 然而,大多數人在他們的職業(職位)中有不止一份工作,而且人們可能會有很多的教育時期和任何數量的聯絡資訊,從使用者和專案之間具有一對多的關係,可以用不同的方式表示:
-
1、在傳統的SQL模型(在SQL:1999之前)中,最常見的標準化 的 表現是將位置、教育和聯絡資訊放在不同的表中,並通過外來鍵引用放在users表中,如圖所示。
-
2、後來版本的SQL標準增加了對結構化資料型別和xml資料的支援;允許將多值資料儲存在單個行中,並支援在這些文件中查詢和索引。這些特性在Oracle、IBM DB2、MS SQL Server和Post‐gre SQL(6、7)進行了不同程度的實現。JSON資料型別也被幾個資料庫所支援,包括IBM DB2、SQL和Postgre SQL。
-
3、第三種選擇是將工作、教育和聯絡資訊作為JSON或XML Document進行編碼,將其儲存在資料庫的文字列上,並讓應用程式對其結構和內容進行優先處理。在這個設定中,通常無法使用資料庫查詢在編碼列內的值。
對於像上面提到的類似簡歷這樣的資料結構,它通常是一個自包含的文件,JSON可能非常合適:參見示例2-1。JSON相較於XML而言要簡單得多,因此也更具吸引力。面向文件的資料庫如Mongo DB、Rethink DB、Couch DB和Espresso都支援這個資料模型。
Example 2-1. Representing a Linked In profile as a JSON document
{ "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" } }
三、JSON
一些開發人員認為,JSON模型減少了應用程式程式碼和儲存層之間的阻抗不匹配(impedance mismatch)。但是,接下來我們將看到,JSON作為一種資料編碼格式也存在一些問題。同時缺乏schema常常被認為是一種優勢。
JSON表示比圖2-1中多表模式具有更好的區域性性。 如果您想在關係資料庫示例中獲取一個簡歷檔案,您需要執行多個查詢(通過user_id查詢每個表),或者在users表及其附屬表之間執行一個混亂的多路JOIN。在JSON表中,所有相關資訊都在一個地方,一個查詢就足夠了。
使用者位置、教育歷史和聯絡資訊的一對多關係可以表示成資料中的樹結構,而json表示的樹結構顯式如圖2-2所示:

圖2-2
四、Many-to-One and Many-to-Many Relationships
在前一節中,以圖2-1為例,區域id和行業id被指定為ID,而不是純文字字串“Greater Seattle Area”和“Philanthropy”。思考下這是為什麼?
如果使用者介面有需要輸入的諸如“區域”或“行業”等文字欄位,那麼沒有將它們(行業和區域)儲存為純文字字串是有意義的。 同時,將地理位置資訊抽取出來組成一張表也是有好處的:
- 1、有助於保持一致的風格和拼寫
- 2、避免歧義 (e.g., if there are several cities with the same name)
- 3、易於更新-the name is stored in only one place, so it is easy to update across the board if it ever needs to be changed (e.g., change of a city name due topolitical events)
- 4、本地化支援—when the site is translated into other languages, the stand‐ardized lists can be localized, so the region and industry can be displayed in theviewer’s language
- 5、方便更好的查詢—e.g., a search for philanthropists in the state of Washington can match this profile, because the list of regions can encode the fact that Seattle is in Washington (which is not apparent from the string "Greater Seattle Area")
是否儲存ID或文字字串主要考慮的是重複問題。 當您使用ID時,對人類有意義的資訊(例如“Philanthropy慈善”)只儲存在一個地方,所有引用的資訊都使用一個ID(在資料庫中只有這個ID)。 當你直接儲存文字時,你將在每個使用它的記錄中複製具有人類意義的文字資訊,儲存的佔用會更高。 使用ID的優點是,因為它對人類沒有意義,所以即使它標識的資訊發生了變化,它也不需要改變。 而任何對人類有意義的事情都可能在未來的某個時候發生變化——如果這些資訊被複制,所有多餘的副本都需要更新 。 這就會導致寫過多的開銷,並且會出現不一致的風險(有些資訊會被更新,而有些則沒有更新)。消除這種重複是資料庫標準化的關鍵思想。
不幸的是對資料進行規範化需要多對一的關係(many-to-one )例如,許多人生活在同一個區域,許多人在同一個行業工作等,此時人與區域,人與行業都是多對一關係,這種多對一關係並不適合文件資料模型(document model)。而在關係型資料庫中通常通過ID引用其他表中的行,因此在關係型資料庫中JOIN很容易。但是在document model中join操作對於一對多(one-to-many)的樹形結構並不是必須的,並且對Join的支援非常差。
如果資料庫本身不支援JOIN,那麼就需要在應用端程式碼中通過多次查詢資料庫模擬JOIN操作(在上面提到的這種情況下,區域和行業列表可能很小,你可以把他們儲存在記憶體中,但是這樣做相當於把JOIN的工作從資料庫中轉移到應用端程式碼中了)
此外,即使應用程式的初始版本很適合無連線的document model,但是隨著應用程式功能的不斷新增,資料會傾向於變得更加具有關聯性。
五、Relational Versus Document Databases Today
當比較關係型資料與Document資料庫時有很多需要考慮的地方,包括他們的容錯性,併發處理能力。這裡我們僅僅對資料模型進行對比。
Document模型的最大優勢是 schema靈活 ,同時對於一些應用而言,他與一些應用程式碼中所使用的資料結構很相似。而關係型資料庫的優勢是提供JOIN和更好的對多對一關係支援,以及多對多的支援。
六、Which data model leads to simpler application code?
如果你的應用的資料結構類似文件(例如一個一對多的樹型結構,且通常一次載入整個樹)那麼Document將非常合適;而關係技術將一個類似文件的結構撕裂成幾個碎片化的表,會導致複雜的schema和複雜的應用程式程式碼。
文件資料庫也有限制,例如你不能直接引用文件中未定義的專案,例如你通常需要這樣描述:“使用者251的職位列表中第二個職位”(很像層級模型中的訪問路徑),然而只要文件沒有太深的巢狀,這通常不是問題。
而文件資料庫對JOIN的支援不足,可能是問題,也可能不是問題,這主要取決於應用。例如利用文件資料庫記錄某個時間點發生的事件並分析的應用中,可能不需要多對多關係。然而如果你的應用中確實需要多對多關係,那麼Document資料庫就不那麼吸引人了。它可能導致需要使用非常規的手段來實現JOIN操作,此外應用程式也需要新增額外的程式碼來保證資料一致性:通過向資料庫傳送多個請求可以在應用端實現JOIN操作,但是這樣會將複雜性轉移到應用程式中,同時,這樣做通常比在資料庫中執行的JOIN要慢。因此在這種情況下,會導致應用程式更加複雜以及更糟糕的效能。
一般來說,無法肯定哪個資料庫一定會導致應用程式更加複雜,它主要取決於資料之間的關係型別,對於高度互聯的資料, 文件資料庫是笨拙的,而關係型資料庫是有效的,圖資料庫是更加自然的 。
七、Schema flexibility in the document model
大多數的document資料庫以及支援JSON的關係型資料庫不會對document中的資料進行任何強制的schema,在關係型資料庫中支援XML時通常帶有可選的schema驗證。當沒有schema時,任意的key和value都可以插入到document中,同時在讀取資料時客戶端對資料也沒有任何保證。
document資料庫通常也被稱為 schemaless ,這樣稱呼有一些誤導的意思,因為在程式碼讀取資料時通常採用某種結構來解析資料,例如是一個隱式的schema,只是沒有被資料庫強制執行。一個更準確的詞應該是: schema-on-read(資料的結構是隱式的,只有當讀取資料時才使用該結構去解析) 。與之相應的 是 schema-on-write(通常在關係型資料庫中被採用,schema是顯示的,資料庫會確保所有的資料都符合schema)
Schema-on-read 類似於程式語言中的動態型別檢查,相反, Schema-on-write 就是靜態型別檢查。正如開發人員對於靜態型別檢查和動態型別檢查有很大爭論一樣 ,資料庫中schema的實現也是一個有很大爭議的額話題,無關沒有對錯。
當應用程式想要更改他的資料格式時,這兩種模式的差異將會非常明顯;假如,當前你正在將每個使用者的全名儲存在一個欄位中,然而不久之後你希望姓和名分開儲存。在document資料庫中,你需要使用一個新的document用來寫first name,同時在應用程式中當讀取老的資料時需要進行如下處理:
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]; }
而另一方面,在靜態型別schema的資料庫中,你需要進行遷移:
ALTER TABLE users ADD COLUMN first_name text; UPDATE users SET first_name = split_part(name, ' ', 1);-- Postgre SQL UPDATE users SET first_name = substring_index(name, ' ', 1);-- My SQL
schema修改的代價是需要停機時間,這個代價是非常昂貴且完全不值得的。大多數的關係型資料在執行 ALTER TABLE語句的耗時約在幾秒內,Mysql是一個明顯的例外, 它可以在ALTER table上覆制整個表,這意味著在修改一個大型表時,可能需要幾分鐘甚至幾個小時的停機時間——儘管有各種各樣的工具在這個限制下工作。
在大表上執行UPDATE語句可能會在任何資料庫上都很慢,因為每一行都需要重寫。通常這是不可接受的,當然你也可以在應用程式中進行處理,將first_name設定為NULL,並在讀取時填充它,就像使用文件資料庫一樣。但是在schema-on-read模式下是危險的,如果一個集合中的所有元素因為一些原因(例如資料是多樣的)導致結構並不是一致,例如:有許多不同型別的物件,將每種型別的物件放到各自的表中是不切實際的,資料的結構是由外部系統決定的,而這些系統是您所控制的,並且隨時可能發生變化。
在這種情況下,一個schema可能會過大於功,而schemaless的記錄則是一個更自然的資料模型。 但是,如果所有的記錄都被期望具有相同的結構,那麼schema是用來記錄和執行結構的有用機制。我們將在之後詳細討論模式和模式演化。
相關參考文件References
[1] Edgar F. Codd: “A Relational Model of Data for Large Shared Data Banks,” Com‐munications of the ACM, volume 13, number 6, pages 377–387, June 1970. doi:10.1145/362384.362685
[2] Michael Stonebraker and Joseph M. Hellerstein: “What Goes Around ComesAround,” in Readings in Database Systems, 4th edition, MIT Press, pages 2–41, 2005.ISBN: 978-0-262-69314-1
[3] Pramod J. Sadalage and Martin Fowler: No SQL Distilled. Addison-Wesley, August2012. ISBN: 978-0-321-82662-6
[4] Eric Evans: “No SQL: What’s in a Name?,” blog.sym-link.com, October 30, 2009.
[5] James Phillips: “Surprises in Our No SQL Adoption Survey,” blog.couchbase.com,February 8, 2012.
[6] Michael Wagner: SQL/XML:2006 – Evaluierung der Standardkonformität ausge‐wählter Datenbanksysteme. Diplomica Verlag, Hamburg, 2010. ISBN:978-3-836-64609-3
[7] “XML Data in SQL Server,” SQL Server 2012 documentation, technet.micro‐soft.com, 2013.
[8] “Postgre SQL 9.3.1 Documentation,” The Postgre SQL Global DevelopmentGroup, 2013.
[9] “The Mongo DB 2.4 Manual,” Mongo DB, Inc., 2013.
[10] “Rethink DB 1.11 Documentation,” rethinkdb.com, 2013.
[11] “Apache Couch DB 1.6 Documentation,” docs.couchdb.org, 2014.
[12] Lin Qiao, Kapil Surlaker, Shirshanka Das, et al.: “On Brewing Fresh Espresso:Linked In’s Distributed Data Serving Platform,” at ACM International Conference onManagement of Data (SIGMOD), June 2013.
[13] Rick Long, Mark Harrington, Robert Hain, and Geoff Nicholls: IMS Primer.IBM Redbook SG24-5352-00, IBM International Technical Support Organization,January 2000.
[14] Stephen D. Bartlett: “IBM’s IMS—Myths, Realities, and Opportunities,” TheClipper Group Navigator, TCG2013015LI, July 2013.
[15] Sarah Mei: “Why You Should Never Use Mongo DB,” sarahmei.com, November11, 2013.
譯者注:
17年初讀此書,驚為神作,年末心(Kai)血(shi)來(zuo)潮(si)利用空閒時間翻譯此書,自(chun)娛(shu)自(zhuang)樂(bi)!歷時8個月完成十之七八後,終告失敗。之後會整理歸納後,希望作為一個系列,在此記錄,聊以自慰。。。
需要提的是,18年9月份國內有三位大神已經成功出版了該書的中文版,有興趣可以去讀下。
未完待續。。。。。。。。。。。。
文章來源《Designing Data-Intensive Applications》翻譯有刪改

微信公眾號