讀古今文學網 > MongoDB實戰 > 1.4 為什麼選擇MongoDB >

1.4 為什麼選擇MongoDB

為什麼MongoDB對於你的項目來說是一個好的選擇?我想我已經給出不少理由了。本節中,我會更明確地進行說明,首先說說MongoDB項目的總體設計目標。根據其作者的觀點,MongoDB的設計是要結合鍵值存儲和關係型數據庫的最好特性。鍵值存儲,因為非常簡單,所以速度極快而且相對容易伸縮。關係型數據庫較難伸縮,至少很難水平伸縮,但擁有富數據模型和強大的查詢語言。如果MongoDB能介於兩者之間,就能成為一款易伸縮、能存儲豐富數據結構、提供複雜查詢機制的數據庫。

在使用場景方面,MongoDB非常適合用做以下應用程序的主要數據存儲:Web應用程序、分析與記錄應用程序,以及任何要求有中等級別緩存的應用程序。此外,由於它能方便地存儲無Schema數據,MongoDB還很適合保存事先無法知曉其數據結構的數據。

之前所說的內容還不太足以讓人信服,為了證實它們,我們大致瞭解一下目前市面上的眾多數據庫,並和MongoDB做個對比。接下來,我將討論一些特殊的MongoDB使用場景,提供一些生產環境中的例子。最後,我還會討論一些MongoDB實際使用中的重要注意事項。

1.4.1 MongoDB與其他數據庫的對比

市面上的數據庫數量成爆炸式增長,要在它們之間進行權衡是很困難的。幸運的是,它們之中的大多數數據庫都能歸在幾個分類裡。本節中,我會描述簡單及複雜的鍵值存儲、關係型數據庫和文檔數據庫,並將它們與MongoDB做一個比較。下面來看表1-1。

表1-1 數據庫家族

示  例數據模型伸縮性模型使用場景 簡單鍵值存儲memcached鍵值對,其中值是一個二進制大字段多種模型。memcached能跨多個節點進行伸縮,把所有可用內存變為一個巨大的數據存儲緩存、Web操作 複雜鍵值存儲Cassandra、Project Voldemort、Riak多種模型。Cassandra使用名為列(column)的鍵值結構。Voldemort存儲二進制大字段最終一致性,多節點部署以獲得高可用性和簡單的故障轉移高吞吐量垂直內容(活動feed、消息隊列)、緩存、Web操作 關係型數據庫Oracle數據庫、MySQL、PostgreSQL數據表垂直伸縮。對集群和手動分區支持有限要求事務(銀行、金融)或SQL的系統、正規化數據模型

1. 簡單鍵值存儲

簡單鍵值存儲正如其名,基於給定的鍵對值做索引。常見的場景是緩存。舉例來說,假設需要緩存一個由應用程序呈現的HTML頁面,此處的鍵可能是頁面的URL,值是HTML本身。請注意,對鍵值存儲而言,值就是一個不透明的字節數組。沒有強加關係型數據庫中的Schema,也沒有任何數據類型的概念。這自然限制了鍵值存儲允許的操作:可以放入一個新值,然後通過鍵將其找出或刪除。擁有如此簡單性的系統通常很快,而且具有可伸縮性。

最著名的簡單鍵值存儲是memcached(發音是mem-cash-dee)。memcached僅在內存裡存儲數據,用持久性來換取速度。它也是分佈式的,跨多台服務器的memcached節點能像單個數據存儲那樣來使用,這消除了維護跨服務器緩存狀態的複雜性。

與MongoDB相比,memcached這樣的簡單鍵值存儲通常讀寫會更快。但與MongoDB不同,這些系統很少能充當主要數據存儲。簡單鍵值存儲的最佳用途是附加存儲,既可以作為傳統數據庫之上的緩存層,也可以作為任務隊列之類的短暫服務的簡單持久層。

2. 複雜鍵值存儲

可以改進簡單鍵值模型來處理複雜的讀寫Schema或提供更豐富的數據模型。如此一來,就有了複雜鍵值存儲。廣為流傳的論文「Dynamo: Amazon』s Highly Available Key-value Store」中描述的亞馬遜 Dynamo就是這樣一個例子。Dynamo旨在成為一個健壯的數據庫,在網絡故障、數據中心停轉及類似情況下仍能工作。這要求系統總是能夠被讀和寫,本質上就是要求數據能自動跨多個節點進行複製。如果一個節點發生故障,系統的用戶(在這裡可能是一個使用亞馬遜購物車的顧客)不會察覺到服務中斷。當系統允許同一份數據被寫到多個節點時,發生衝突的情況是不可避免的,Dynamo提供了一些解決衝突的方法。與此同時,Dynamo也很容易伸縮。因為沒有主節點,所有節點都是對等的,所以很容易從整體上理解系統,能方便地添加節點。儘管Dynamo是一個私有系統,但其構建理念啟發了很多NoSQL系統,包括Cassandra、Project Voldemort和Riak。

看看是誰開發了這些複雜鍵值存儲,看看實踐中它們的使用情況如何,你就能知道它們的優點了。以Cassandra為例,它實現了很多Dynamo的伸縮屬性,同時還提供了與谷歌 BigTable類似的面向列的數據模型。Cassandra是一款開源的數據存儲,是Facebook為其收件箱搜索功能開發的。該系統可以水平擴展,索引超過50 TB的收件箱數據,允許在收件箱中對關鍵字和收件人做檢索。數據是根據用戶ID做索引的,每條記錄由一個用於關鍵字檢索的搜索項數組和一個用於收件人檢索的收件人ID數組構成。1

1. 參見http://mng.bz/5321。

這些複雜鍵值存儲是由亞馬遜、谷歌和Facebook這樣的大型互聯網公司開發的,用來管理系統的多個部分,擁有非常大的數據量。換言之,複雜鍵值存儲管理了一個相對自包含的域,它對海量存儲和可用性有一定要求。由於採用了無主節點的架構,這些系統能輕鬆地通過添加節點進行擴展。它們都選擇了最終一致性,也就是說讀請求不必返回最後一次寫的內容。用戶用較弱的一致性所換得的是在某一節點失效時仍能寫入的能力。

這與MongoDB正好相反,MongoDB提供了強一致性、(每個分片)一個主節點、更豐富的數據模型,還有二級索引,最後兩項特性總是一起出現的。如果一個系統允許跨多個域建模,例如構建完整Web應用程序時就會有此要求,那麼查詢就需要跨整個數據模型,這時就要用到二級索引了。

因為有豐富的數據模型,可以考慮把MongoDB作為更通用的大型、可伸縮Web應用程序的解決方案。MongoDB的伸縮架構有時也會受到非難,因為它並非源自Dynamo。但針對不同域有不同的伸縮解決方案。MongoDB的自動分片受到了雅虎PNUTS數據存儲和谷歌 BigTable的啟發。讀過展示這些數據存儲技術的白皮書的人會發現,Mongodb實現伸縮的方法已經有成功的案例。

3. 關係型數據庫

本章已經介紹了不少關係型數據庫的內容,簡單起見,我只討論RDBMS與MongoDB的相同點和不同點。儘管MySQL2使用固定Schema的數據表,MongoDB使用無Schema的文檔,但兩者都能表示豐富的數據模型。MySQL和MongoDB都支持B樹索引,那些適用於MySQL索引的經驗也同樣適用於MongoDB。MySQL支持聯結和事務,因此如果你必須使用SQL或者要求有事務,那麼只能選擇MySQL或其他RDBMS。也就是說,MongoDB的文檔模型足以在不用聯結查詢的情況下表示對象。MongoDB中對單獨文檔的更新也是原子的,這提供了傳統事務的一個子集。MongoDB和MySQL都支持複製。就可伸縮性而言,MongoDB設計成能水平擴展,能自動分片並處理故障轉移。MySQL上的分片都需要手動管理,有一定的複雜性,更常見的是垂直擴展的MySQL系統。

2. 這裡我用MySQL來做說明,因為我所描述的特性適用於大多數關係型數據庫。

4. 文檔數據庫

自稱為文檔數據庫的產品還不多,在本書編寫時,除了MongoDB之外,唯一的著名文檔型數據庫就是Apache CouchDB。儘管CouchDB的數據是使用JSON格式的純文本存儲的,而MongoDB是使用BSON二進制格式,但兩者的文檔模型是相似的。與MongoDB一樣,CouchDB也支持二級索引,不同之處是CouchDB中的索引是通過編寫MapReduce函數來定義的,這比MySQL和MongoDB使用的聲明式語法更複雜一些。兩者伸縮的方式也有所不同,CouchDB不會把數據分散到多台服務器上,每個CouchDB節點都是其他節點的完整副本。

1.4.2 使用場景和生產部署

老實說,我們不會僅根據數據庫的特性做選擇,還需要知道使用它的真實成功案例。這裡,我提供一些廣義上的MongoDB使用場景,以及一些生產環境中的示例3。

3. 要想獲得在生產環境中使用了MongoDB的最新案例列表,請訪問http://mng.bz/z2CH。

1.Web應用程序

MongoDB很適合作為Web應用程序的主要數據存儲。就算是一個簡單的Web應用程序也會有很多數據模型,用來管理用戶、會話、應用特定的數據、上傳和權限,更不用說非常重要的域了。正如它們能和關係型數據庫的表列數據配合良好一樣,它們也能獲益於MongoDB的集合與文檔模型。因為文檔能表示豐富的數據結構,建模相同數據所需的集合數量通常會比使用完全正規化關係型模型的數據表數量要少。此外,動態查詢和二級索引能讓你輕鬆地實現SQL開發者所熟悉的大多數查詢。最後,作為一個成長中的Web應用程序,MongoDB提供了清晰的擴展路線。

在生產環境中,MongoDB已經證明它能管理應用的方方面面,從主要數據領域到附加數據存儲,比如日誌和實時分析。這裡的案例來自The Business Insider(TBI),它從2008年1月起將MongoDB作為主要數據存儲使用。雖然TBI是一個新聞網站,但它流量很大,每天有超過一百萬獨立頁面訪問。這個案例中有意思的是除了處理站點的主要內容(文章、評論、用戶等),MongoDB還處理並存儲實時分析數據。這些分析被TBI用於生成動態熱點地圖,標明不同新聞故事的點擊率。該站目前還沒有太多的數據,因此尚不需要分片,但它有使用副本集來保證停電時的自動故障轉移。

2. 敏捷開發

無論如何看待敏捷開發運動,你都很難否認對於快速構建應用程序的渴望。不少開發團隊,包括Shutterfly和紐約時報的團隊,都部分選擇了MongoDB,因為相比關係型數據庫,使用MongoDB能更快地開發應用程序。一個明顯的原因是MongoDB沒有固定的Schema,所有花在提交、溝通和實施Schema變更的時間都省下來了。

除此之外,不再需要花時間把數據的關係型表述硬塞進面向對象的數據模型裡去了,也不用處理ORM生成的SQL的奇怪行為,或者對它做優化了。如此一來,MongoDB為項目帶來了更短的開發週期和敏捷的、中等大小的團隊。

3.分析和日誌

我之前已經暗示過MongoDB適用於分析和日誌,將MongoDB用於這些方面的應用程序數量增長得很快。通常,發展成熟的公司都會選擇將用於分析的特殊應用作為切入點,進入MongoDB的世界。這些公司包括GitHub、Disqus、Justin.tv和Gilt Groupe,還有其他公司就不再列舉了。

MongoDB與分析的關聯源自於它的速度和兩個關鍵特性:目標原子更新和固定集合(capped collection)。原子更新讓客戶端能高效地增加計數器,將值放入數組。固定集合,常用於日誌,特點是分配的大小固定,能實現自動過期。相比文件系統,將日誌數據保存在數據庫裡更易組織,而且能提供更強大的查詢能力。現在,拋開grep或自定義日誌檢索工具,用戶可以使用他們熟悉並喜歡的MongoDB查詢語言來查看日誌輸出。

4.緩存

這是一種數據模型,它能更完整地表示對象,結合更快的平均查詢速度,經常讓MongoDB介於傳統的MySQL與memcached之間。例如之前提到的TBI,它可以不使用memcached,直接通過MongoDB來響應頁面請求。

5. 可變Schema

看看這段代碼示例4:

4. 這個想法來自於http://mng.bz/52XI。如果想運行這段代碼,需要把-umongodb:secret替換成自己的Twitter用戶名和密碼。

curl https://stream.twitter.com/1/statuses/sample.json -umongodb:secret
| mongoimport -c tweets
  

這裡從Twitter流上拉下一小段示例,並用管道將其直接導入MongoDB集合。因為流生成的是JSON文檔,在把它發給數據庫前就不需要預先處理數據了。mongoimport工具能直接將數據轉換成BSON。這意味著每條推文都能保持其結構,原封不動地存儲為集合中的單個文檔。無論你是想查詢、索引還是執行MapReduce聚合,都能立刻操作數據。而且,不需要事先聲明數據的結構。

如果應用程序需要調用JSON API,那麼擁有這樣一個能輕鬆轉換JSON的系統就太棒了。如果在存儲之前無法預先瞭解數據的結構,MongoDB沒有Schema約束的特性能大大簡化數據模型。