MongoDB是一個面向文檔的資料庫,目前由10gen開發並維護,它的功能豐富,齊全,完全可以替代MySQL。在使用MongoDB做產品原型的過程中,我們總結了MonogDB的一些亮點:
使用JSON風格文法,易於掌握和理解:MongoDB使用JSON的變種BSON作為內部儲存的格式和文法。針對MongoDB的操作都使用JSON風格文法,用戶端提交或接收的資料都使用JSON形式來展現。相對於SQL來說,更加直觀,容易理解和掌握。
Schema-less,支援嵌入子文檔:MongoDB是一個Schema-free的文檔資料庫。一個資料庫可以有多個Collection,每個Collection是Documents的集合。Collection和Document和傳統資料庫的Table和Row並不對等。無需事先定義Collection,隨時可以建立。
Collection中可以包含具有不同schema的文檔記錄。 這意味著,你上一條記錄中的文檔有3個屬性,而下一條記錄的文檔可以有10個屬性,屬性的類型既可以是基本的資料類型(如數字、字串、日期等),也可以是數組或者散列,甚至還可以是一個子文檔(embed document)。這樣,可以實現逆正常化(denormalizing)的資料模型,提高查詢的速度。
圖1 MongoDB是一個Schema-free的文檔資料庫
圖2是一個例子,作品和評論可以設計為一個collection,評論作為子文檔內嵌在art的comments屬性中,評論的回複則作為comment子文檔的子文檔內嵌於replies屬性。按照這種設計模式,只需要按照作品id檢索一次,即可獲得所有相關的資訊了。在MongoDB中,不強調一定對資料進行Normalize ,很多場合都建議De-normalize,開發人員可以扔掉傳統關聯式資料庫各種範式的限制,不需要把所有的實體都映射為一個Collection,只需定義最頂級的class。MongoDB的文檔模型可以讓我們很輕鬆就能將自己的Object映射到collection中實現儲存。
圖2 MongoDB支援嵌入子文檔
簡單易用的查詢方式:MongoDB中的查詢讓人很舒適,沒有SQL難記的文法,直接使用JSON,相當的直觀。對不同的開發語言,你可以使用它最基本的數組或散列格式進行查詢。配合附加的operator,MongoDB支援範圍查詢,Regex查詢,對子文檔內屬性的查詢,可以取代原來大多數任務的SQL查詢。
CRUD更加簡單,支援in-place update:只要定義一個數組,然後傳遞給MongoDB的insert/update方法就可自動插入或更新;對於更新模式,MongoDB支援一個upsert選項,即:“如果記錄存在那麼更新,否則插入”。MongoDB的update方法還支援Modifier,通過Modifier可實現在服務端即時更新,省去用戶端和服務端的通訊。這些modifer可以讓MongoDB具有和Redis、Memcached等KV類似的功能:較之MySQL,MonoDB更加簡單快速。Modifier也是MongoDB可以作為對使用者行為跟蹤的容器。在實際中使用Modifier來將使用者的互動行為快速儲存到MongoDB中以便後期進行統計分析和個人化定製。
所有的屬性類型都支援索引,甚至數組:這可以讓某些任務實現起來非常的輕鬆。在MongoDB中,“_id”屬性是主鍵,預設MongoDB會對_id建立一個唯一索引。
服務端指令碼和Map/Reduce:MongoDB允許在服務端執行指令碼,可以用Javascript編寫某個函數,直接在服務端執行,也可以把函數的定義儲存在服務端,下次直接調用即可。MongoDB不支援事務層級的鎖定,對於某些需要自訂的“原子性”操作,可以使用Server side指令碼來實現,此時整個MongoDB處於鎖定狀態。Map/Reduce也是MongoDB中比較迷人的特性。Map/Reduce可以對大資料量的表進行統計、分類、合并的工作,完成原先SQL的GroupBy等彙總函式的功能。並且Mapper和Reducer的定義都是用Javascript來定義服務端指令碼。
效能高效,速度快: MongoDB使用c++/boost編寫,在多數場合,其查詢速度對比MySQL要快的多,對於CPU佔用非常小。部署也很簡單,對大多數系統,只需下載後二進位包解壓就可以直接運行,幾乎是零配置。
支援多種複製模式: MongoDB支援不同的伺服器間進行複製,包括雙機互備的容錯方案。
Master-Slave是最常見的。通過Master-Slave可以實現資料的備份。在我們的實踐中,我們使用的是Master-Slave模式,Slave只用於後備,實際的讀寫都是從Master節點執行。
Replica Pairs/Replica Sets允許2個MongoDB相互監聽,實現雙機互備的容錯。
MongoDB只能支援有限的雙主模式(Master-Master),實際可用性不強,可忽略。
內建GridFS,支援大容量的儲存:這個特點是最吸引我眼球的,也是讓我放棄其他NoSQL的一個原因。GridFS具體實現其實很簡單,本質仍然是將檔案分塊後儲存到files.file和files.chunk 2個collection中,在各個主流的driver實現中,都封裝了對於GridFS的操作。由於GridFS自身也是一個Collection,你可以直接對檔案的屬性進行定義和管理,通過這些屬性就可以快速找到所需要的檔案,輕鬆管理海量的檔案,無需費神如何hash才能避免檔案系統檢索效能問題, 結合下面的Auto-sharding,GridFS的擴充能力是足夠我們使用了。在實踐中,我們用MongoDB的GridFs儲存圖片和各種尺寸的縮圖。
圖3 MongoDB的Auto-sharding結構
內建Sharding,提供基於Range的Auto Sharding機制:一個collection可按照記錄的範圍,分成若干個段,切分到不同的Shard上。Shards可以和複製結合,配合Replica sets能夠實現Sharding+fail-over,不同的Shard之間可以負載平衡。查詢是對用戶端是透明的。用戶端執行查詢,統計,MapReduce等操作,這些會被MongoDB自動路由到後端的資料節點。這讓我們關注於自己的業務,適當的時候可以無痛的升級。MongoDB的Sharding設計能力最大可支援約20 petabytes,足以支撐一般應用。
第三方支援豐富: MongoDB社區非常活躍,很多開發架構都迅速提供了對MongDB的支援。不少知名大公司和網站也在生產環境中使用MongoDB,越來越多的創新型企業轉而使用MongoDB作為和Django,RoR來搭配的技術方案。
實施結果
實施MonoDB的過程是令人愉快的。我們對自己的PHP開發架構進行了修改以適應MongoDB。在PHP中,對MongoDB的查詢、更新都是圍繞Array進行的,實現代碼變得很簡潔。由於無需建表,MonoDB運行測試單元所需要的時間大大縮短,對於TDD敏捷開發的效率也提高了。當然,由於MongoDB的文檔模型和關聯式資料庫有很大不同,在實踐中也有很多的困惑,幸運的是,MongoDB開源社區給了我們很大協助。最終,我們使用了2周就完成了從MySQL到MongoDB的代碼移植比預期的開發時間大大縮短。從我們的測試結果看也是非常驚人,資料量約2千萬,資料庫300G的情況下,讀寫2000rps,CPU等系統消耗是相當的低(我們的資料量還偏小,目前陸續有些公司也展示了他們的經典案例:MongoDB儲存的資料量已超過 50億,>1.5TB)。目前,我們將MongoDB和其他服務共同部署在一起,大大節約了資源。
一些小提示
切實領會MongoDB的Document模型,從實際出發,扔掉關聯式資料庫的範式思維定義,重新設計類;在服務端啟動並執行JavaScript代碼避免使用遍曆記錄這種耗時的操作,相反要用Map/Reduce來完成這種表資料的處理;屬性的類型插入和查詢時應該保持一致。若插入時是字串“1”,則查詢時用數字1是不匹配的;最佳化MongoDB的效能可以從磁碟速度和記憶體著手;MongoDB對每個Document的限制是最大不超過4MB;在符合上述條件下多啟用Embed Document, 避免使用DatabaseReference;內部緩衝可以避免N+1次查詢問題(MongoDB不支援joins)。
用Capped Collection解決需要高速寫入的場合,如即時日誌;大資料量情況下,建立同步時要調高oplogSize的大小,並且自己預先產生資料檔案,避免出現用戶端逾時;Collection+Index合計數量預設不能超過24000;目前的版本(<v1.6)刪除資料的空間不能被回收,如果你頻繁刪除資料,那麼需要定期執行repairDatabase,釋放這些空間。
結束語
MongoDB的裡程碑是1.6版本,預計今年7月份發布,屆時,MongoDB的Sharding將首次具備在生產環境中使用的條件。作為MongoDB的受益者,我們目前也在積极參与MongoDB社區活動,改進Perl/PHP對於MongoDB的技術方案。在1.6版本後也將年內推出基於MongoDB的一些開源項目。
對於那些剛剛起步,或者正在開發創新型互連網應用的公司來說,MongoDB的快速、靈活、輕量和強大擴充性,正適合我們快速開發產品,快速迭代,適應使用者迅速變化和更新的種種需求。
總而言之,MongoDB是一個最適合替代MySQL的全功能的NoSQL產品,使用MongoDB+Perl/PHP/Django/RoR的組合將很快成為開發Web2.0、3.0的產品的最佳組合,就像當年MySQL替代Oracle/DB2/Informix一樣,曆史總是驚人的相似,讓我們拭目以待吧!
作者簡介:http://hi.baidu.com/ixigua
潘凡(nightsailer,N.S.), 視覺中國網站技術總監,聯合創始人,家有1狗2貓。目前負責網站平台設計和底層產品研發工作。當前關註:Apps平台設計、分布式檔案儲存體、NoSQL、高效能後現代的Perl編程。Twitter:@nightsailer Blog:http://nightsailer.com/