現(xiàn)在你應(yīng)該有感覺(jué),何時(shí)何地把 MongoDB 融入你現(xiàn)有的系統(tǒng)是最棒的了。這有超多的新的類似的存儲(chǔ)技術(shù),肯定會(huì)讓你在選擇的時(shí)候暈頭轉(zhuǎn)向。
對(duì)我來(lái)說(shuō),最重要的教訓(xùn),跟 MongoDB 無(wú)關(guān),是說(shuō)你不用再依賴單一的解決案來(lái)處理你的數(shù)據(jù)了。毫無(wú)疑問(wèn),一個(gè)單一的解決案有明顯的優(yōu)勢(shì),對(duì)于許多項(xiàng)目來(lái)說(shuō) - 或者說(shuō)大多數(shù) - 單一解決案是一個(gè)明智的選擇。意思不是說(shuō)你?必須?使用不同的技術(shù),而是說(shuō)你?可以。 只有你自己才知道,引進(jìn)新技術(shù)是否利大于弊。
說(shuō)了那么多,我希望你到目前為止學(xué)到知識(shí)讓你覺(jué)得 MongoDB 是一個(gè)通用的解決案。我們已經(jīng)提到很多次了,面向文檔的數(shù)據(jù)庫(kù)和關(guān)系型數(shù)據(jù)庫(kù)有很多方面類似。因此,與其繞開(kāi)這些相同點(diǎn),不如我們可以簡(jiǎn)單的這樣認(rèn)為, MongoDB 是關(guān)系型數(shù)據(jù)庫(kù)的一個(gè)代替案。比如說(shuō)用 Lucene 作為關(guān)系型數(shù)據(jù)庫(kù)的全文檢索索引的加強(qiáng),或者用 Redis 作為持久型 key-value 存儲(chǔ),MongoDB 就是用來(lái)保存你的數(shù)據(jù)的。
注意,我沒(méi)有說(shuō)用 MongoDB?取代?關(guān)系型數(shù)據(jù)庫(kù),而是?代替?案。它能做的有很多工具也能做。有些事情 MongoDB 可以做的更好,另外一些 MongoDB 做得差點(diǎn)。我們來(lái)進(jìn)一步來(lái)討論一下。
面向文檔數(shù)據(jù)庫(kù)經(jīng)常吹噓的一個(gè)好處就是,它不需要一個(gè)固定的模式。這使得他們比傳統(tǒng)的數(shù)據(jù)庫(kù)表要靈活得多。我同意無(wú)模式是一個(gè)很不錯(cuò)的特性,但不是大多數(shù)人說(shuō)的那樣。
人們講到無(wú)模式的時(shí)候,好像你就會(huì)把一堆亂七八糟的數(shù)據(jù)統(tǒng)統(tǒng)存起來(lái)一樣。確實(shí)有些領(lǐng)域有些數(shù)據(jù)用關(guān)系型數(shù)據(jù)庫(kù)來(lái)建模很痛苦,不過(guò)我覺(jué)得這些都是不常見(jiàn)的特例。無(wú)模式是酷,可是大多數(shù)情況下你的數(shù)據(jù)結(jié)構(gòu)還是應(yīng)當(dāng)好好設(shè)計(jì)的。真正需要處理混亂時(shí)是不錯(cuò),比如當(dāng)你添加一個(gè)新功能的時(shí)候,不過(guò)事實(shí)是,大多數(shù)情況下,一個(gè)空列基本可以解決問(wèn)題。
對(duì)我來(lái)說(shuō),動(dòng)態(tài)模式的真正好處在于無(wú)需很多設(shè)置以及可以降低在 OOP 中使用的阻力。這在你使用靜態(tài)語(yǔ)言的時(shí)候尤其明顯。我在 C# 和 Ruby 中用過(guò) MongoDB ,差異非常明顯。Ruby 的動(dòng)態(tài)特性以及它的流行的 ActiveRecord 實(shí)現(xiàn),已經(jīng)大幅降低面向?qū)ο?關(guān)系開(kāi)發(fā)之間差異所帶來(lái)的阻力。這不是說(shuō) MongoDB 和 Ruby 不配,而是是說(shuō)它們太配了。真的,我覺(jué)得許多 Ruby 開(kāi)發(fā)者眼中的的 MongoDB 只是有些許改進(jìn)而已,而在 C# 或者 Java 開(kāi)發(fā)者眼中,MongoDB 帶來(lái)的是處理數(shù)據(jù)交互方式的翻天覆地變化。
假設(shè)從驅(qū)動(dòng)開(kāi)發(fā)者角度來(lái)看這個(gè)問(wèn)題。你想保存一個(gè)對(duì)象?把它串行化成 JSON (嚴(yán)格來(lái)說(shuō)是 BSON, 不過(guò)差不多) 然后把它傳給 MongoDB。不需要做任何屬性映射或者類型映射。這種簡(jiǎn)單性的好處就這樣傳遞給了你,終端開(kāi)發(fā)者。
MongoDB 可以勝任的一個(gè)特殊角色是在日志領(lǐng)域。有兩點(diǎn)使得 MongoDB 的寫操作非??臁J紫?,你可以選擇發(fā)送了寫操作命令之后立刻返回,而無(wú)須等到操作完成。其次,你可以控制數(shù)據(jù)持久性的寫行為。這些設(shè)置,加上,可以定義一個(gè)成功的提交,需要在多少臺(tái)服務(wù)器上成功拿到你的數(shù)據(jù)之后才算成功,并且每個(gè)寫操作都是可設(shè)置, 這就給予你很高的權(quán)限用以控制寫性能和數(shù)據(jù)持久性。
除了這些性能因素,日志數(shù)據(jù)還是這樣一種數(shù)據(jù)集,用無(wú)模式集合更有優(yōu)勢(shì)。最后,MongoDB 還提供了?受限集合(capped collection)。到目前為止,所有我們默認(rèn)創(chuàng)建的集合都是普通集合。我們可以通過(guò)?db.createCollection
?命令來(lái)創(chuàng)建一個(gè)受限集合并標(biāo)記它的限制:
//limit our capped collection to 1 megabyte
db.createCollection('logs', {capped: true,
size: 1048576})
當(dāng)我們的受限集合到達(dá) 1MB 上限的時(shí)候,舊文檔會(huì)被自動(dòng)清除。另外一種限制可以基于文檔個(gè)數(shù),而不是大小,用?max
標(biāo)記。受限集合有一些非常有趣的屬性。比如說(shuō),你可以更新文檔但是你不能改變它的大小。插入順序是被設(shè)置好了的,因此不需要另外提供一個(gè)索引來(lái)獲取基于時(shí)間的排序,你可以 "tail" 一個(gè)受限集合,就和你在 Unix 中通過(guò)?tail -f
?來(lái)處理文件一樣,獲取最新的數(shù)據(jù),如果存在數(shù)據(jù)的話,而不需要重新查詢它。
如果想讓你的數(shù)據(jù) "過(guò)期" ,基于時(shí)間而不是整個(gè)集合的大小,你可以用?TTL 索引?,所謂 TTL 是 "time-to-live" 的縮寫。
在 1.8 之前的版本,MongoDB 不支持單服務(wù)器持久性。就是說(shuō),如果一個(gè)服務(wù)器崩潰了,可能會(huì)導(dǎo)致數(shù)據(jù)的丟失或者損壞。解決案是在多服務(wù)器上運(yùn)行 MongoDB 副本 (MongoDB 支持復(fù)制)。日志(Journaling)是 1.8 版追加的一個(gè)非常重要的功能。從 2.0 版的 MongoDB 開(kāi)始,日志是默認(rèn)啟動(dòng)的,該功能允許快速恢復(fù)服務(wù)器,比如遭遇到了服務(wù)器崩潰或者停電的情況。
持久性在這里只是提一下,因?yàn)閲@ MongoDB 過(guò)去缺乏單服務(wù)器持久的問(wèn)題,人們?nèi)〉昧吮姸喑晒?。這個(gè)話題在以后的 Google 檢索中也許還會(huì)繼續(xù)出現(xiàn)。但是關(guān)于缺少日志功能這一缺點(diǎn)的信息,都是過(guò)時(shí)了的。
真正的全文檢索是在最近加入到 MongoDB 中的。它支持十五國(guó)語(yǔ)言,支持詞形變化(stemming)和干擾字(stop words)。除了原生的 MongoDB 的全文檢索支持,如果你需要一個(gè)更強(qiáng)大更全面的全文檢索引擎的話,你需要另找方案。
MongoDB 不支持事務(wù)。這有兩個(gè)代替案,一個(gè)很好用但有限制,另外一個(gè)比較麻煩但靈活。
第一個(gè)方案,就是各種原子更新操作。只要能解決你的問(wèn)題,都挺不錯(cuò)。我們已經(jīng)看過(guò)幾個(gè)簡(jiǎn)單的了,比如?$inc
?和$set
。還有像?findAndModify
?命令,可以更新或刪除文檔之后,自動(dòng)返回修改過(guò)的文檔。
第二個(gè)方案,當(dāng)原子操作不能滿足的時(shí)候,回到兩段提交上來(lái)。對(duì)于事務(wù),兩段提交就好像給鏈接手工解引用。這是一個(gè)和存儲(chǔ)無(wú)關(guān)的解決方案。兩段提交實(shí)際上在關(guān)系型數(shù)據(jù)庫(kù)世界中非常常用,用來(lái)實(shí)現(xiàn)多數(shù)據(jù)庫(kù)之間的事務(wù)。 MongoDB 網(wǎng)站?有個(gè)例子?演示了最典型的場(chǎng)合 (資金轉(zhuǎn)賬)。通常的想法是,把事務(wù)的狀態(tài)保存到實(shí)際的原子更新的文檔中,然后手工的進(jìn)行 init-pending-commit/rollback 處理。
MongoDB 支持內(nèi)嵌文檔以及它靈活的 schema 設(shè)計(jì),讓兩步提交沒(méi)那么痛苦,但是它仍然不是一個(gè)好處理,特別是當(dāng)你剛開(kāi)始接觸它的時(shí)候。
在2.2 版本之前的 MongoDB 依賴 MapReduce 來(lái)解決大部分?jǐn)?shù)據(jù)處理工作。在 2.2 版本,它追加了一個(gè)強(qiáng)力的功能,叫做aggregation framework or pipeline,因此你只要對(duì)那些尚未支持管道的,需要使用復(fù)雜方法的,不常見(jiàn)的聚合使用 MapReduce。下一章我們將看看聚合管道和 MapReduce 的細(xì)節(jié)?,F(xiàn)在,你可以把他們想象成功能強(qiáng)大的,用不同方法實(shí)現(xiàn)的?group by
?(打個(gè)比方)。對(duì)于非常大的數(shù)據(jù)的處理,你可能要用到其他的工具,比如 Hadoop。值得慶幸的是,這兩個(gè)系統(tǒng)是相輔相成的,這里有個(gè)?MongoDB connector for Hadoop。
當(dāng)然,關(guān)系型數(shù)據(jù)庫(kù)也不擅長(zhǎng)并行數(shù)據(jù)處理。MongoDB 有計(jì)劃在未來(lái)的版本中,改善增加處理大數(shù)據(jù)集的能力。
一個(gè)很強(qiáng)大的功能就是 MongoDB 支持?geospatial 索引。這允許你保存 geoJSON 或者 x 和 y 坐標(biāo)到文檔,并查詢文檔,用如?$near
?來(lái)獲取坐標(biāo)集,或者?$within
?來(lái)獲取一個(gè)矩形或圓中的點(diǎn)。這個(gè)特性最好通過(guò)一些可視化例子來(lái)演示,所以如果你想學(xué)更多的話,可以試試看?5 minute geospatial interactive tutorial。
你應(yīng)該已經(jīng)知道這個(gè)問(wèn)題的答案了,MongoDB 確實(shí)比大多數(shù)的關(guān)系型數(shù)據(jù)要年輕很多。這個(gè)問(wèn)題確實(shí)是你應(yīng)當(dāng)考慮的,但是到底有多重要,這取決于你要做什么,怎么做。不管怎么說(shuō),一個(gè)好的評(píng)估,不可能忽略 MongoDB 年輕這一事實(shí),而可用的工具也不是很好 (雖然成熟的關(guān)系型數(shù)據(jù)庫(kù)工具有些也非常渣!)。舉個(gè)例子,它缺乏對(duì)十進(jìn)制浮點(diǎn)數(shù)的支持,在處理貨幣的系統(tǒng)來(lái)說(shuō),明顯是一個(gè)問(wèn)題 (盡管也不是致命的) 。
積極的一方面,它為大多數(shù)語(yǔ)言提供了驅(qū)動(dòng),協(xié)議現(xiàn)代而簡(jiǎn)約,開(kāi)發(fā)速度相當(dāng)快。MongoDB 被眾多公司用到了生產(chǎn)環(huán)境中,雖然有所擔(dān)心,但經(jīng)過(guò)驗(yàn)證后,擔(dān)心很快就變成了過(guò)去。
本章要說(shuō)的是,MongoDB,大多數(shù)情況下,可以取代關(guān)系型數(shù)據(jù)庫(kù)。它更簡(jiǎn)單更直接;更快速并且通常對(duì)應(yīng)用開(kāi)發(fā)者的約束更少。不過(guò)缺乏事務(wù)支持也許值得慎重考慮。當(dāng)人們說(shuō)起?MongoDB 在新的數(shù)據(jù)庫(kù)陣營(yíng)中到底處在什么位置??時(shí),答案很簡(jiǎn)單:?中庸(2)。
更多建議: