在今天脓匿,最著名的數(shù)據(jù)模型莫過于SQL了吧在岂。這是一個Codd在1970年提出的關系模型:數(shù)據(jù)被組織成關系(稱為SQL中的表)考余,其中每個關系都是無序的元組集合(SQL中的行)杏死。
關系模型是一個理論上的設想,在當時很多人都懷疑它是否能有效地實施煌寇。然而焕蹄,到上世紀80年代中期,關系數(shù)據(jù)庫管理系統(tǒng)和SQL已經(jīng)成為大多數(shù)需要使用某種常規(guī)結構來存儲和查詢數(shù)據(jù)的人的首選工具阀溶,關系數(shù)據(jù)庫的主導地位也持續(xù)了25年之久腻脏。
關系數(shù)據(jù)庫的根源在于業(yè)務數(shù)據(jù)處理鸦泳,在20世紀60年代和70年代這些其實是在大型計算機上執(zhí)行的。而從今天的角度來看永品,用例看起來也十分普通:典型的事務處理(進入銷售或銀行事務做鹰、航空公司預訂、倉庫中的庫存)和批處理(客戶發(fā)票鼎姐、工資钾麸、報表)。當時的其他數(shù)據(jù)庫還在迫使應用程序開發(fā)人員考慮數(shù)據(jù)庫中數(shù)據(jù)的內(nèi)部表示症见,而關系模型的目標卻是在一個更干凈的接口后面隱藏實現(xiàn)細節(jié)喂走。
多年來殃饿,人們都在比較不同的數(shù)據(jù)存儲和查詢的方法谋作。比如,在20世紀70年代和80年代早期乎芳,網(wǎng)絡模型和層次模型是主要的替代方案遵蚜,但是關系模型開始主導它們。在20世紀80年代末和90年代初奈惑,對象數(shù)據(jù)庫再次出現(xiàn)吭净。而XML數(shù)據(jù)庫出現(xiàn)在21世紀初,但也只看到了特定的應用肴甸。關系模型的每個競爭對手都在其時代產(chǎn)生了大量的炒作寂殉,但從未持續(xù)過2次。
隨著計算機變得更加強大和網(wǎng)絡化原在,它們開始被用于日益多樣化的用途友扰。值得注意的是,關系數(shù)據(jù)庫在最初的業(yè)務數(shù)據(jù)處理范圍之外庶柿,開始被廣泛應用于各種用例村怪。今天在web上看到的大部分內(nèi)容仍然由關系數(shù)據(jù)庫驅(qū)動,比如在線發(fā)布浮庐、討論甚负、社交網(wǎng)絡、電子商務审残、游戲梭域、SAAS應用程序,甚至更多搅轿。
NOSQL的誕生
而現(xiàn)在碰辅,NoSQL是顛覆關系模式主導地位的最新嘗試〗槭保“NoSQL”這個名字很不幸没宾,因為它實際上并不指代任何一種特定的技術——它最初只是作為一個吸引人的Twitter標簽凌彬,用在2009年的一場關于開源分布式非關系型數(shù)據(jù)庫的meetup。然而循衰,這個術語引起了很多人的關注铲敛,并迅速在網(wǎng)絡創(chuàng)業(yè)社區(qū)和其他地方傳播開來。現(xiàn)在会钝,許多有趣的數(shù)據(jù)庫系統(tǒng)都與nosql關聯(lián)在一起伐蒋,而且它還被重新解釋為不僅僅是SQL。
采用NoSQL數(shù)據(jù)庫的背后有幾個驅(qū)動因素迁酸,主要包括:
????1.大家需要一個相比于關系數(shù)據(jù)庫來說先鱼,具有更大的可伸縮性,包括非常大的數(shù)據(jù)集或非常高的寫入吞吐量的系統(tǒng)奸鬓。
????2.相比于商業(yè)數(shù)據(jù)庫產(chǎn)品焙畔,人們當然更偏愛于免費和開源軟件
????3.一些特殊的查詢操作,這是關系模型沒有很好支持的
????4.對關系模型限制性的失望串远,以及對更動態(tài)宏多、更富表現(xiàn)力的數(shù)據(jù)模型的渴望
不同的應用程序有不同的需求,對于一個用例來說澡罚,最好的技術選擇可能與另一個用例的最佳選擇是不同的伸但。因此,在可預見的將來留搔,關系數(shù)據(jù)庫將繼續(xù)與各種非關系型數(shù)據(jù)存儲一起使用更胖。
對象-關系不匹配
在當今,大多數(shù)應用程序開發(fā)都是用面向?qū)ο蟮木幊陶Z言完成的隔显,這導致了對SQL數(shù)據(jù)模型的常見批評:如果數(shù)據(jù)存儲在關系表中却妨,應用程序代碼中的對象和表、行和列的數(shù)據(jù)庫模型之間需要一個笨拙的轉(zhuǎn)換層荣月。模型之間的脫節(jié)有時被稱為阻抗失配管呵。
像Hibernate這樣的對象-關系映射(ORM)框架減少了這個翻譯層所需的樣板代碼的數(shù)量,但是它們不能完全隱藏這兩個模型之間的差異哺窄。
例如捐下,圖2-1演示了如何在關系模式中表達簡歷(LinkedIn個人資料)。整個概要文件可以通過唯一標識符userid來標識萌业。像firstname和lastname這樣的字段在每個用戶中出現(xiàn)一次坷襟,因此它們可以被建模為用戶表中的列。然而生年,大多數(shù)人的職業(yè)生涯(職位)都不止一份工作婴程,而且人們的受教育年限和接觸信息的數(shù)量也各不相同。由此可見抱婉,這是一個一對多的關系档叔,我們也可以用不同的方式表示:
? ? 1.在傳統(tǒng)SQL模型中(SQL1999)桌粉,最常見的表達方式是將位置、教育衙四、和聯(lián)系信息存在單獨的表中铃肯,然后跟用戶表建立外鍵引用,如圖2-1所示传蹈。
? ? 2.后來的SQL標準版本增加了對結構化數(shù)據(jù)類型和XML數(shù)據(jù)的支持押逼;這使得多值數(shù)據(jù)可以被存儲在一行中,并支持在這些文檔中查詢和索引惦界。這些特性在Oracle挑格、IBM DB2、SQL Server和PostgreSQL中都得到了不同程度的支持沾歪。JSON數(shù)據(jù)類型也受到幾個數(shù)據(jù)庫的支持漂彤,包括IBM DB2、MySQL和PostgreSQL瞬逊。
? ? 3.第三種選擇是將工作显歧、教育和聯(lián)系信息轉(zhuǎn)化成JSON或XML文檔仪或,將其存儲在數(shù)據(jù)庫的文本列中确镊,并讓應用程序解釋其結構和內(nèi)容。在這種設置中范删,通常不能使用數(shù)據(jù)庫直接查詢列內(nèi)的值蕾域。
對于像簡歷這樣的數(shù)據(jù)結構來說到旦,它是一個自成體系的文檔旨巷,所以用JSON表示非常合適:參見例2-1。JSON的表達比XML要簡單得多添忘。面向文檔的數(shù)據(jù)庫采呐,如MongoDB、RethinkDB搁骑、CouchDB和Espresso斧吐,都支持這種數(shù)據(jù)模型。
例2-1仲器,將LinkedIn個人資料用一個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" }}
一些開發(fā)人員認為JSON模型減少了應用程序代碼和存儲層之間的阻抗失配煤率。然而,正如我們將在第4章中看到的乏冀,JSON作為一種數(shù)據(jù)編碼格式也存在問題蝶糯。缺乏schema通常被認為是一種優(yōu)勢,我們將在后續(xù)“文檔模型的模式靈活性”中討論這一點辆沦。
與圖2-1中的多表模式相比昼捍,JSON很明顯更加適合识虚。如果您想要在關系示例中獲取profile,您需要執(zhí)行多條查詢(通過userid查詢每個表)妒茬,或者在users表及其下屬表之間的做join舷礼。在JSON表示中,所有相關的信息都在一個地方郊闯,一個查詢就足夠了妻献。
從用戶profile到用戶的位置、教育歷史和聯(lián)系信息的一對多關系团赁,我們可以看出這是數(shù)據(jù)中的樹結構育拨,而JSON能夠顯示的表達此樹結構(參見圖2-2)。
多對一和多對多關系
在前一節(jié)中欢摄,以2-1為例熬丧,區(qū)域id和行業(yè)id被作為id,而不是純文本字符串“大西雅圖地區(qū)”和“慈善”怀挠。為什么析蝴?
如果用戶界面有用于輸入?yún)^(qū)域和行業(yè)字段的文本框,那么將它們存儲為純文本字符串是有意義的绿淋。但是闷畸,擁有一個標準化的地理區(qū)域和行業(yè)列表,并讓用戶從下拉列表或自動完成列表中進行選擇是有一定的優(yōu)勢的吞滞,具體如下:
? ? 1.一致的風格和拼寫
? ? 2.避免模棱兩可(例如佑菩,如果有幾個同名的城市)
? ? 3.易于更新——名稱只存儲在一個地方,因此裁赠,如果需要更改的話殿漠,它很容易進行更新(例如,由于政治事件而更改城市名稱)佩捞。
????4.本地化支持——當站點被翻譯成其他語言時绞幌,標準化的列表可以本地化,因此區(qū)域和行業(yè)可以瀏覽者的本地語言來顯示一忱。
? ? 5.更好的搜索莲蜘。
是否存儲ID或文本字符串是一個關于重復的問題。當您使用一個ID時掀潮,對人類有意義的信息(例如慈善這個詞)只存儲在一個地方菇夸,并且所有引用它的信息都使用一個ID(它只在數(shù)據(jù)庫中有意義)。當您直接存儲文本時仪吧,您將在使用它的每條記錄中復制有意義的信息庄新。
使用ID的優(yōu)點是,由于它對人沒有意義,所以它永遠不需要改變:ID可以保持不變择诈,即使它標識的信息發(fā)生了變化械蹋。任何對人類有意義的事情都需要在將來的某個時候改變——如果這些信息是重復的,那么所有多余的副本都需要更新羞芍。這就會導致寫管理費用哗戈,并存在不一致的風險(一些信息的副本會被更新,而其他的則不會)荷科。消除這種重復是數(shù)據(jù)庫中標準化背后的關鍵思想唯咬。
不幸的是,規(guī)范化這種數(shù)據(jù)需要多對一關系(許多人生活在一個特定的區(qū)域畏浆,許多人在一個特定的行業(yè)工作)胆胰,這與文檔模型不太匹配。在關系型數(shù)據(jù)庫中刻获,用ID引用其他表中的行是很正常的蜀涨,因為join很容易。在文檔數(shù)據(jù)庫中蝎毡,通常不需要對一對多的樹結構進行join厚柳,而且對join的支持常常是弱的。
如果數(shù)據(jù)庫本身不支持join沐兵,那么您必須通過對數(shù)據(jù)庫進行多次查詢來模擬應用程序代碼中的join别垮。(在這種情況下,區(qū)域和行業(yè)的列表可能很小痒筒,而且變化很慢宰闰,因此應用程序可以簡單地將它們保存在內(nèi)存中茬贵。但是簿透,創(chuàng)建join的工作從數(shù)據(jù)庫轉(zhuǎn)移到應用程序代碼。)
此外解藻,即使應用程序的初始版本很適合 join-free的文檔模型老充,數(shù)據(jù)也會隨著特性被添加到應用程序中而變得更加相互關聯(lián)。例如螟左,考慮一下我們可以對簡歷示例進行一些更改:
組織和學校作為實體
在前面的描述中啡浊,組織(用戶工作的公司)和學校名稱(他們學習的地方)只是字符串。也許它們應該是對實體的引用胶背?然后巷嚣,每個組織、學星鳎或大學都可以擁有自己的網(wǎng)頁(帶有徽標廷粒、新聞提要等);每個簡歷都可以鏈接到它提到的組織和學校,并包含他們的標志和其他信息(見圖2-3坝茎,來自LinkedIn的一個例子)涤姊。
建議
假設您想要添加一個新特性:一個用戶可以為另一個用戶寫推薦信。這些建議在被推薦的用戶的簡歷上顯示嗤放,并附上推薦用戶的姓名和照片思喊。如果推薦者更新他們的照片,他們所寫的任何推薦都需要反映新照片次酌。因此恨课,這些建議應該有一個針對作者的個人資料的引用。
圖2-4說明了這些新特性為什么需要多對多關系庄呈。每個點狀矩形中的數(shù)據(jù)可以被分組到一個文檔中,但是對組織派阱、學校和其他用戶的引用需要被表示為引用诬留,并且在查詢時需要join。