1. 程式人生 > >關於Mongodb的全面總結,學習mongodb的人,可以從這裡開始!

關於Mongodb的全面總結,學習mongodb的人,可以從這裡開始!

原文地址:http://blog.csdn.net/jakenson/article/details/7060431

 MongoDB的內部構造《MongoDB The Definitive Guide》

MongoDB的官方文件基本是how to do的介紹,而關於how it worked卻少之又少,本人也剛買了《MongoDB TheDefinitive Guide》的影印版,還沒來得及看,本文原作者將其書中一些關於MongoDB內部現實方面的一些知識介紹如下,值得一看。

今天下載了《MongoDB The Definitive Guide》電子版,瀏覽了裡面的內容,還是挺豐富的。是官網文件實際應用方面的一個補充。和官方文件類似,介紹MongoDB的內部原理是少之又少,只有在附錄的一個章節中介紹了相關內容。

對於大多數的MongoDB的使用者來說,MongoDB就像是一個大黑盒,但是如果你能夠了解到MongoDB內部一些構造的話,將有利於你更好地理解和使用MongoDB。

BSON

在MongoDB中,文件是對資料的抽象,它被使用在Client端和Server端的互動中。所有的Client端(各種語言的Driver)都會使用這種抽象,它的表現形式就是我們常說的BSON(Binary JSON )。

BSON是一個輕量級的二進位制資料格式。MongoDB能夠使用BSON,並將BSON作為資料的儲存存放在磁碟中。

當Client端要將寫入文件,使用查詢等等操作時,需要將文件編碼為BSON格式,然後再發送給Server端。同樣,Server端的返回結果也是編碼為BSON格式再放回給Client端的。

使用BSON格式出於以下3種目的:

效率

BSON是為效率而設計的,它只需要使用很少的空間。即使在最壞的情況下,BSON格式也比JSON格式再最好的情況下儲存效率高。

傳輸性

在某些情況下,BSON會犧牲額外的空間讓資料的傳輸更加方便。比如,字串的傳輸的字首會標識字串的長度,而不是在字串的末尾打上結束的標記。這樣的傳輸形式有利於MongoDB修改傳輸的資料。

效能

最後,BSON格式的編碼和解碼都是非常快速的。它使用了C風格的資料表現形式,這樣在各種語言中都可以高效地使用。

寫入協議

Client端訪問Server端使用了輕量級的TCP/IP寫入協議。這種協議在

MongoDB Wiki中有詳細介紹,它其實是在BSON資料上面做了一層簡單的包裝。比如說,寫入資料的命令中包含了1個20位元組的訊息頭(由訊息的長度和寫入命令標識組成),需要寫入的Collection名稱和需要寫入的資料。

資料檔案

在MongoDB的資料資料夾中(預設路徑是/data/db)由構成資料庫的所有檔案。每一個數據庫都包含一個.ns檔案和一些資料檔案,其中資料檔案會隨著資料量的增加而變多。所以如果有一個數據庫名字叫做foo,那麼構成foo這個資料庫的檔案就會由foo.ns,foo.0,foo.1,foo.2等等組成。

資料檔案每新增一次,大小都會是上一個資料檔案的2倍,每個資料檔案最大2G。這樣的設計有利於防止資料量較小的資料庫浪費過多的空間,同時又能保證資料量較大的資料庫有相應的空間使用。

MongoDB會使用預分配方式來保證寫入效能的穩定(這種方式可以使用–noprealloc關閉)。預分配在後臺進行,並且每個預分配的檔案都用0進行填充。這會讓MongoDB始終保持額外的空間和空餘的資料檔案,從而避免了資料增長過快而帶來的分配磁碟空間引起的阻塞。

名字空間和盤區

每一個數據庫都由多個名字空間組成,每一個名字空間儲存了相應型別的資料。資料庫中的每一個Collection都有各自對應的名字空間,索引檔案同樣也有名字空間。所有名字空間的元資料都儲存在.ns檔案中。

名字空間中的資料在磁碟中分為多個區間,這個叫做盤區。在下圖中,foo這個資料庫包含3個數據檔案,第三個資料檔案屬於空的預分配檔案。頭兩個資料檔案被分為了相應的盤區對應不同的名字空間。

上圖顯示了名字空間和盤區的相關特點。每一個名字空間可以包含多個不同的盤區,這些盤區並不是連續的。與資料檔案的增長相同,每一個名字空間對應的盤區大小的也是隨著分配的次數不斷增長的。這樣做的目的是為了平衡名字空間浪費的空間與保持某一個名字空間中資料的連續性。上圖中還有一個需要注意的名字空間:$freelist,這個名字空間用於記錄不再使用的盤區(被刪除的Collection或索引)。每當名字空間需要分配新的盤區的時候,都會先檢視$freelist是否有大小合適的盤區可以使用。

記憶體對映儲存引擎

MongoDB目前支援的儲存引擎為記憶體對映引擎。當MongoDB啟動的時候,會將所有的資料檔案對映到記憶體中,然後作業系統會託管所有的磁碟操作。這種儲存引擎有以下幾種特點:

* MongoDB中關於記憶體管理的程式碼非常精簡,畢竟相關的工作已經有作業系統進行託管。

* MongoDB伺服器使用的虛擬記憶體將非常巨大,並將超過整個資料檔案的大小。不用擔心,作業系統會去處理這一切。

* MongoDB無法控制資料寫入磁碟的順序,這樣將導致MongoDB無法實現writeahead日誌的特性。所以,如果MongoDB希望提供一種durability的特性(這一特性可以參考我寫的關於Cassandra文章:http://www.cnblogs.com/gpcuster/tag/Cassandra/),需要實現另外一種儲存引擎。

* 32位系統的MongoDB伺服器每一個Mongod例項只能使用2G的資料檔案。這是由於地址指標只能支援32位。

其他

在《MongoDB The Definitive Guide》中介紹的MongoDB內部構造只有這麼多,如果真要把它說清楚,可能需要另外一本書來專門講述了。比如內部的JS解析,查詢的優化,索引的建立等等。有興趣的朋友可以直接參考原始碼

MongoDB的架構

當前架構                           雙伺服器架構


當前架構為單shard+replica Set模式,雙伺服器為雙Shard+Replica Set模式。同一個Shard中的primary和Secondary儲存內容一致。而雙Shard則是兩個Shard分散式儲存不同資料,備份由shard內部進行。

雙伺服器中的兩個Shard各含一個primary ,一個secondary,和一個arbiter(arbiter的唯一作用是在primary 宕機後選舉新的primary時擁有投票權,用以使存活節點數大於50%,不包括50%,否則系統將整個down掉,以及在票數相同的情況下用以打破選舉的平衡,並不儲存和讀取資料)。

因為同一個shard中,只有primary可以用以寫,secondary只是用於對primary節點的備份並用於讀操作,然後再primary宕機的情況下接管它的工作。所以,雙shard模式下,兩個伺服器分別包含一個primary,而且同一個shard的arbiter必須和secondary在一個伺服器上。這樣子既保證了兩個伺服器都可以進行讀、寫操作,而且在primary down的時候也能夠繼續使得選取成功secondary。

後續擴充套件時,可以再在叢集中新增新的shard,然後與老的shard進行balance均衡操作。

MongoDB的特點

MongoDB 是一個面向集合的,模式自由的文件型資料庫.

面向集合, 意思是資料被分組到若干集合,這些集合稱作聚集(collections). 在資料庫裡每個聚集有一個唯一的名字,可以包含無限個文件. 聚集是RDBMS中表的同義詞,區別是聚集不需要進行模式定義.

模式自由, 意思是資料庫並不需要知道你將存入到聚集中的文件的任何結構資訊.實際上,你可以在同一個聚集中儲存不同結構的文件.

文件型, 意思是我們儲存的資料是鍵-值對的集合,鍵是字串,值可以是資料型別集合裡的任意型別,包括陣列和文件. 我們把這個資料格式稱作 "[BSON]"即 "Binary Serialized dOcument Notation."

u  面向文件儲存:(類JSON資料模式簡單而強大)。

u  高效的傳統儲存方式:支援二進位制資料及大型物件(如照片和視訊)。

u  複製及自動故障轉移:Mongo資料庫支援伺服器之間的資料複製,支援主-從模式及伺服器之間的相互複製。

u  Auto-Sharding自動分片支援雲級擴充套件性(處於早期alpha階段):自動分片功能支援水平的資料庫叢集,可動態新增額外的機器。

u  動態查詢:它支援豐富的查詢表示式。查詢指令使用JSON形式的標記,可輕易查詢文件中內嵌的物件及陣列。

u  全索引支援:包括文件內嵌物件及陣列。Mongo的查詢優化器會分析查詢表示式,並生成一個高效的查詢計劃。

u  支援RUBY,PYTHON,JAVA,C++,PHP等多種語言。

u  面向集合儲存,易儲存物件型別的資料:儲存在集合中的文件,被儲存為鍵-值對的形式。鍵用於唯一標識一個文件,為字串型別,而值則可以是各中複雜的檔案型別;

u  *模式自由:儲存在mongodb資料庫中的檔案,我們不需要知道它的任何結構定義;

u  *支援完全索引,包含內部物件。

u  *支援複製和故障恢復。

u  *自動處理碎片: 自動分片功能支援水平的資料庫叢集,可動態新增額外的機器

u 查詢監視:Mongo包含一個監視工具用於分析資料庫操作的效能

MongoDB的功能

       查詢:基於查詢物件或者類SQL語句搜尋文件. 查詢結果可以排序,進行返回大小限制,可以跳過部分結果集,也可以返回文件的一部分.

       插入和更新 : 插入新文件,更新已有文件.

       索引管理 : 對文件的一個或者多個鍵(包括子結構)建立索引,刪除索引等等

常用命令: 所有MongoDB 操作都可以通過socket傳輸的DB命令來執行.

MongoDB的侷限性與不足

本文來源於對Quora上一個問答的整理,主要列舉了MongoDB身上一些侷限的功能及目前做得不夠好的地方。其中包括了原本就並非MongoDB想做的部分,也包括了MongoDB想做但沒做好的方面。

  • 在32位系統上,不支援大於2.5G的資料。詳見這裡
  • 單個文件大小限制為 4 M/16 M(1.8版本後升為16M)
  • 鎖粒度太粗,MongoDB使用的是一把全域性的讀寫鎖,詳見這裡
  • 不支援join操作和事務機制,這個確實是非MongoDB要做的領域
  • 對記憶體要求比較大,至少要保證熱資料(索引,資料及系統其它開銷)都能裝進記憶體
  • 使用者許可權方面比較弱,這一點MongoDB官方推薦的是將機器部署在安全的內網環境中,儘量不要用許可權,詳見這裡
  • MapReduce在單個例項上無法並行,只有採用Auto-Sharding才能並行。這是由JS引擎的限制造成的
  • MapReduce的結果無法寫入到一個被Sharding的Collection中,2.0版本對這個問題的解決好像也不徹底
  • 對於陣列型的資料操作不夠豐富
  • Auto-Sharding還存在很多問題,所謂的水平擴充套件也不是那麼理想

適用範圍

u  適合實時的插入,更新與查詢,並具備應用程式實時資料儲存所需的複製及高度伸縮性。

u  適合作為資訊基礎設施的持久化快取層。

u  適合由數十或數百臺伺服器組成的資料庫。因為Mongo已經包含對MapReduce引擎的內建支援。

u  Mongo的BSON資料格式非常適合文件化格式的儲存及查詢。

網站資料:Mongo非常適合實時的插入,更新與查詢,並具備網站實時資料儲存所需的複製及高度伸縮性。

u  ◆快取:由於效能很高,Mongo也適合作為資訊基礎設施的快取層。在系統重啟之後,由Mongo搭建的持久化快取層可以避免下層的資料來源過載。

u  ◆大尺寸,低價值的資料:使用傳統的關係型資料庫儲存一些資料時可能會比較昂貴,在此之前,很多時候程式設計師往往會選擇傳統的檔案進行儲存。

u  ◆高伸縮性的場景:Mongo非常適合由數十或數百臺伺服器組成的資料庫。Mongo的路線圖中已經包含對MapReduce引擎的內建支援。

u  ◆用於物件及JSON資料的儲存:Mongo的BSON資料格式非常適合文件化格式的儲存及查詢

MongoDB的不適用範圍

·        高度事務性的系統。

·        傳統的商業智慧應用。

·        級為複雜的SQL查詢。

·        ◆高度事務性的系統:例如銀行或會計系統。傳統的關係型資料庫目前還是更適用於需要大量原子性複雜事務的應用程式。

·        ◆傳統的商業智慧應用:針對特定問題的BI資料庫會對產生高度優化的查詢方式。對於此類應用,資料倉庫可能是更合適的選擇。

·        ◆需要SQL的問題

u   

要點

跟mysqld一樣,一個mongod服務可以有建立多個數據庫,每個資料庫可以有多張表,這裡的表名叫collection,每個collection可以存放多個文件(document),每個文件都以BSON(binary json)的形式存放於硬碟中。跟關係型資料庫不一樣的地方是,它是的以單文件為單位儲存的,你可以任意給一個或一批文件新增或刪除欄位,而不會對其它文件造成影響,這就是所謂的schema-free,這也是文件型資料庫最主要的優點。跟一般的key-value資料庫不一樣的是,它的value中儲存了結構資訊,所以你又可以像關係型資料庫那樣對某些域進行讀寫、統計等操作。可以說是兼備了key-value資料庫的方便高效與關係型資料庫的強大功能。

索引

跟關係型資料庫類似,mongodb可以對某個欄位建立索引,可以建立組合索引、唯一索引,也可以刪除索引。當然建立索引就意味著增加空間開銷,我的建議是,如果你能把一個文件作為一個物件的來考慮,在線上應用中,你通常只要對物件ID建立一個索引即可,根據ID取出物件某些資料放在memcache即可。如果是後臺的分析需要,響應要求不高,查詢非索引的欄位即便直接掃表也費不了太多時間。如果還受不了,就再建一個索引得了。

預設情況下每個表都會有一個唯一索引:_id,如果插入資料時沒有指定_id,服務會自動生成一個_id,為了充分利用已有索引,減少空間開銷,最好是自己指定一個unique的key為_id,通常用物件的ID比較合適,比如商品的ID。

capped collection是一種特殊的表,它的建表命令為:

db.createCollection("mycoll",{capped:true, size:100000})

允許在建表之初就指定一定的空間大小,接下來的插入操作會不斷地按順序APPEND資料在這個預分配好空間的檔案中,如果已經超出空間大小,則回到檔案頭覆蓋原來的資料繼續插入。這種結構保證了插入和查詢的高效性,它不允許刪除單個記錄,更新的也有限制:不能超過原有記錄的大小。這種表效率很高,它適用於一些暫時儲存資料的場合,比如網站中登入使用者的session資訊,又比如一些程式的監控日誌,都是屬於過了一定的時間就可以被覆蓋的資料。

複製與分片

mongodb的複製架構跟mysql也很類似,除了包括master-slave構型和master-master構型之外,還有一個Replica pairs構型,這種構型在平常可以像master-slave那樣工作,一但master出現問題,應用會自動了連線slave。要做複製也很簡單,我自己使用過master-slave構型,只要在某一個服務啟動時加上–master引數,而另一個服務加上–slave與–source引數,即可實現同步。

分片是個很頭疼的問題,資料量大了肯定要分片,mysql下的分片正是成為無數DBA的噩夢。在mongodb下,文件資料庫類似key-value資料庫那樣的易分佈特性就顯現出來了,無論構造分片服務,新增節點還是刪除節點都非常容易實現。但mongodb在這方面做還不足夠成熟,現在分片的工作還只做到alpha2版本(mongodb v1.1),估計還有很多問題要解決,所以只能期待,就不多說了。

效能

在我的使用場合下,千萬級別的文件物件,近10G的資料,對有索引的ID的查詢不會比mysql慢,而對非索引欄位的查詢,則是全面勝出。mysql實際無法勝任大資料量下任意欄位的查詢,而mongodb的查詢效能實在讓我驚訝。寫入效能同樣很令人滿意,同樣寫入百萬級別的資料,mongodb比我以前試用過的couchdb要快得多,基本10分鐘以下可以解決。補上一句,觀察過程中mongodb都遠算不上是CPU殺手。

gridfs是mongodb一個很有趣的類似檔案系統的東西,它可以用一大塊檔案空間來存放大量的小檔案,這個對於儲存web2.0網站中常見的大量小檔案(如大量的使用者頭像)特別有效。使用起來也很方便,基本上跟一般的檔案系統類似。

mongodb的文件裡提到的user case包括實時分析、logging、全文搜尋,國內也有人使用mongodb來儲存分析網站日誌,但我認為mongodb用來處理有一定規模的網站日誌其實並不合適,最主要的就是它佔空間過於虛高,原來1G的日誌資料它可以存成幾個G,如此下去,一個硬碟也存不了幾天的日誌。另一方面,資料量大了肯定要考慮sharding,而mongodb的sharding到現在為止仍不太成熟。由於日誌的不可更新性的,往往只需APPEND即可,又因為對日誌的操作往往只集中於一兩列,所以最合適作為日誌分析的還是列儲存型的資料庫,特別是像infobright那樣的為資料倉庫而設計的列儲存資料庫。

由於mongodb不支援事務操作,所以事務要求嚴格的系統(如果銀行系統)肯定不能用它。

一、主從配置(Master Slave)

    主從資料庫需要兩個資料庫節點即可,一主一從(並不一定非得兩臺獨立的伺服器,可使用--dbpath引數指定資料庫目錄)。一個從節點可以有多個主節點,這種情況下,local.sources中會有多條配置資訊。一臺伺服器可以同時即為主也為從。如果一臺從節點與主節點不同步,比如從節點的資料更新遠遠跟不上主節點或者從節點中斷之後重啟但主節點中相關的資料更新日誌卻不可用了。這種情況下,複製操作將會終止,需要管理者的介入,看是否預設需要重啟複製操作。管理者可以使用{resync:1} 命令重啟複製操作,可選命令列引數 --autoresync可使從節點在不同步情況發生10秒鐘之後,自動重啟複製操作。如果指定了--autoresync引數,從節點在10分鐘以內自動重新同步資料的操作只會執行一次。
--oplogSize命令列引數(與--master一同使用)配置用於儲存給從節點可用的更新資訊佔用的磁碟空間(M為單位),如果不指定這個引數,預設大小為當前可用磁碟空間的5%(64位機器最小值為1G,32位機器為50M)。

二、互為主從(Replica Pairs
資料庫自動協調某個時間點上的主從關係。開始的時候,資料庫會判斷哪個是從哪個是主,一旦主伺服器負載過高,另一臺就會自動成為主伺服器。
remoteserver組中的其他伺服器host,可加:port指定埠。
arbiterserver 仲裁(arbiter )的host,也可指定埠。仲裁是一臺mongodb伺服器,用於協助判斷某個時間點上的資料庫主從關係。如果同組伺服器在同一個交換機或相同的ec2可用區域內,就沒必要使用仲裁了。如果同組伺服器之間不能通訊,可是使用執行在第三方機器上的仲裁,使用“搶七”方式有效地敲定主伺服器,也可不使用仲裁,這樣所有的伺服器都假定是主伺服器狀態,可通過命令人工檢測當前哪臺資料庫是主資料庫:
$ ./mongo
> db.$cmd.findOne({ismaster:1});
{ "ismaster" : 0.0 , "remote" : "192.168.58.1:30001" , "ok" : 1.0 }
一致性:故障轉移機制只能夠保障組中的資料庫上的資料的最終一致性。如果機器L是主伺服器,然後掛了,那麼發生在它身上的最後幾秒鐘的操作資訊就到達不了機器R,那麼機器R在機器L恢復之前是不能執行這些操作的。
安全性:同主從的操作相同。
資料庫伺服器替換。當一臺伺服器失敗了,系統能自動線上恢復。但當一臺機器徹底掛了,就需要替換機器,而替換機器一開始是沒有資料的,怎麼辦?以下會解釋如何替換一組伺服器中的一臺機器。

MongoDB語法與現有關係型資料庫SQL語法比較

MongoDB語法                                  MySql語法

db.test.find({'name':'foobar'})<==> select * from test where name='foobar'

db.test.find()                            <==> select *from test

db.test.find({'ID':10}).count()<==> select count(*) from test where ID=10

db.test.find().skip(10).limit(20)<==> select * from test limit 10,20

db.test.find({'ID':{$in:[25,35,45]}})<==> select * from test where ID in (25,35,45)

db.test.find().sort({'ID':-1})  <==> select * from test order by IDdesc

db.test.distinct('name',{'ID':{$lt:20}})  <==> select distinct(name) from testwhere ID<20

db.test.group({key:{'name':true},cond:{'name':'foo'},reduce:function(obj,prev){prev.msum+=obj.marks;},initial:{msum:0}})  <==> select name,sum(marks) from testgroup by name

db.test.find('this.ID<20',{name:1})  <==> select name from test whereID<20

db.test.insert({'name':'foobar','age':25})<==>insertinto test ('name','age') values('foobar',25)

db.test.remove({})                        <==> delete * from test

db.test.remove({'age':20})            <==> delete test where age=20

db.test.remove({'age':{$lt:20}})   <==> elete test where age<20

db.test.remove({'age':{$lte:20}})  <==> delete test where age<=20

db.test.remove({'age':{$gt:20}})  <==> delete test where age>20

db.test.remove({'age':{$gte:20}})<==> delete test where age>=20

db.test.remove({'age':{$ne:20}})  <==> delete test where age!=20

db.test.update({'name':'foobar'},{$set:{'age':36}})<==> update test set age=36 where name='foobar'

db.test.update({'name':'foobar'},{$inc:{'age':3}})<==> update test set age=age+3 where name='foobar'

進行了一下Mongodb億級資料量的效能測試,分別測試如下幾個專案:
(所有插入都是單執行緒進行,所有讀取都是多執行緒進行)
1) 普通插入效能 (插入的資料每條大約在1KB左右)
2) 批量插入效能 (使用的是官方C#客戶端的InsertBatch),這個測的是批量插入效能能有多少提高
3) 安全插入功能 (確保插入成功,使用的是SafeMode.True開關),這個測的是安全插入效能會差多少
4) 查詢一個索引後的數字列,返回10條記錄(也就是10KB)的效能,這個測的是索引查詢的效能
5) 查詢兩個索引後的數字列,返回10條記錄(每條記錄只返回20位元組左右的2個小欄位)的效能,這個測的是返回小資料量以及多一個查詢條件對效能的影響
6) 查詢一個索引後的數字列,按照另一個索引的日期欄位排序(索引建立的時候是倒序,排序也是倒序),並且Skip100條記錄後返回10條記錄的效能,這個測的是Skip和Order對效能的影響
7) 查詢100條記錄(也就是100KB)的效能(沒有排序,沒有條件),這個測的是大資料量的查詢結果對效能的影響
8) 統計隨著測試的進行,總磁碟佔用,索引磁碟佔用以及資料磁碟佔用的數量

MongoDB CEO:NoSQL的大資料量處理能力

為MongoDB提供技術支援的10gen公司CEO凱文-賴安Dwight Merriman說:“我們公司成立於3月29日,我認為我們選擇的不是一個縫隙市場,相反,我認為我們會慢慢改變企業使用者市場。現在我們可以看到,MongoDB.org網站每月的下載量達到了3萬次,而幾個月前,下載量還為零”。

10gen公司CEO Dwight Merriman

MongoDB的名字源自一個形容詞humongous(巨大無比的),在向上擴充套件和快速處理大資料量方面,它會損失一些精度,在舊金山舉行的MondoDB大會上,Merriman說:“你不適宜用它來處理複雜的金融事務,如證券交易,資料的一致性可能無法得到保證”。

NoSQL資料庫都被貼上不同用途的標籤,如MongoDB和CouchDB都是面向文件的資料庫,但這並不意味著它們可以象JSON(JavaScript ObjectNotation,JavaScript物件標記)那樣以結構化資料形式儲存文字文件。

JSON被認為是XML的代替品,它是一個輕量級的,基於文字交換資料的標準,和XML一樣具有人類易讀的特性。簡單的JSON資料結構叫做物件,可能包括多種資料型別,如整型(int),字串(string),陣列(array),日期(date),物件(object)和位元組陣列(bytearray)。

面向文件的資料庫與關係資料庫有著顯著的區別,面向文件的資料庫用一個有組織的檔案來儲存資料,而不是用行來儲存資料,在MongoDB中,一組文件被看作是一個集合,在關係資料庫中,許多行的集合被看作是一張表。

但同時它們的操作又是類似的,關係資料庫使用select,insert,update和delete操作表中的資料,面向文件的資料庫使用query,insert,update和remove做意義相同的操作。

MongoDB中物件的最大尺寸被限制為4MB,但物件的數量不受限制,MongoDB可以通過叢集加快操作的執行速度,當資料庫變得越來越大時,可以向叢集增加伺服器解決效能問題。

Wordnik工程副總裁Tony Tam說他的公司有5百萬個文件,以前儲存在MySQL資料庫中,大約有1.5TB,一個月前遷移到MongoDB上了,Wordnik專門收集所有單詞的定義和資訊,因此資料量是非常大的,遷移到MongoDB後,Tony Tam說他感到更放心。

Tam說使用MySQL資料庫時,Wordnik專案一直都象是在顛簸的路上前行,資料表的凍結時間有時甚至超過了10秒,這是任何人都不能容忍的。每天會有大約200個新單詞出現,我們要負責收集,並要向資料庫增加1500個例子顯示它們的用法,我們希望寫入資料庫的時間只需要1秒。Tam說:“我們不關心一致性,前後兩個使用者的查詢結果不一定非得保持一致,我們本來就是時刻在做著更新,這一點我們無法保證”。

Wordnik系統就象是一個龐大的線上詞典,有很多人同時線上查詢,但同時我們也在做更新,使用MongoDB後,我們可以保持高速新增資料,不用擔心資料庫會出現堵塞。Tam在MondoDB大會上曾做過一個題為“Wordnik:從MySQL到MongoDB”的演講,他說他們公司只花了一天時間就從MySQL遷移到MongoDB上了。

延伸閱讀

Mongo是一個高效能,開源,無模式的文件型資料庫,它在許多場景下可用於替代傳統的關係型資料庫或鍵/值儲存方式。Mongo使用C++開發,提供了以下功能:

◆面向集合的儲存:適合儲存物件及JSON形式的資料。

◆動態查詢:Mongo支援豐富的查詢表示式。查詢指令使用JSON形式的標記,可輕易查詢文件中內嵌的物件及陣列。

◆完整的索引支援:包括文件內嵌物件及陣列。Mongo的查詢優化器會分析查詢表示式,並生成一個高效的查詢計劃。

◆查詢監視:Mongo包含一個監視工具用於分析資料庫操作的效能。

◆複製及自動故障轉移:Mongo資料庫支援伺服器之間的資料複製,支援主-從模式及伺服器之間的相互複製。複製的主要目標是提供冗餘及自動故障轉移。

◆高效的傳統儲存方式:支援二進位制資料及大型物件(如照片或圖片)。

◆自動分片以支援雲級別的伸縮性(處於早期alpha階段):自動分片功能支援水平的資料庫叢集,可動態新增額外的機器。

MongoDB的主要目標是在鍵/值儲存方式(提供了高效能和高度伸縮性)以及傳統的RDBMS系統(豐富的功能)架起一座橋樑,集兩者的優勢於一身。根據官方網站的描述,Mongo適合用於以下場景:

◆網站資料:Mongo非常適合實時的插入,更新與查詢,並具備網站實時資料儲存所需的複製及高度伸縮性。

◆快取:由於效能很高,Mongo也適合作為資訊基礎設施的快取層。在系統重啟之後,由Mongo搭建的持久化快取層可以避免下層的資料來源過載。

◆大尺寸,低價值的資料:使用傳統的關係型資料庫儲存一些資料時可能會比較昂貴,在此之前,很多時候程式設計師往往會選擇傳統的檔案進行儲存。

◆高伸縮性的場景:Mongo非常適合由數十或數百臺伺服器組成的資料庫。Mongo的路線圖中已經包含對MapReduce引擎的內建支援。

◆用於物件及JSON資料的儲存:Mongo的BSON資料格式非常適合文件化格式的儲存及查詢。

自然,MongoDB的使用也會有一些限制,例如它不適合:

◆高度事務性的系統:例如銀行或會計系統。傳統的關係型資料庫目前還是更適用於需要大量原子性複雜事務的應用程式。

◆傳統的商業智慧應用:針對特定問題的BI資料庫會對產生高度優化的查詢方式。對於此類應用,資料倉庫可能是更合適的選擇。

◆需要SQL的問題

MongoDB支援OS X、Linux及Windows等作業系統,並提供了Python,PHP,Ruby,Java及C++語言的驅動程式,社群中也提供了對Erlang及.NET等平臺的驅動程式。(

漫畫:MongoDB身上的優勢和劣勢

SQL or NoSQL?That’s a question!SQL 與 NoSQL 的爭論從來沒有停息過,但其實任何一種技術都不會是適合一切應用場景的,重要的是你要充分了解自己的需求,再充分了解你要選擇的技術的優劣。

下面是一個關於 MongoDB 優缺點的列表,希望對打算使用 MongoDB 的同學,能有一些作用:

優勢:

快速!(當然,這和具體的應用方式有關,通常來說,它比一般的關係型資料庫快5位左右。)

很高的可擴充套件性 – 輕輕鬆鬆就可實現PB級的儲存(但是可能我們並不需要PB級的儲存,10TB可能就夠了)

他有一個很好的 replication 模式 (replica sets)

有很完善的Java API

他的儲存格式是Json的,這對Java來說非常好處理,對javascirpt亦然。

運維起來非常方便,你不用專門為它安排一個管理員。

它有一個非常活躍的社群(我提出的一個bug在20分鐘內就能得到修復。多謝Elliot)

他的版本控制非常清楚。

MongoDB 背後的公司(10gen)已經準備好了明天在 MongoDB 上面的投入的資金了。

劣勢

應用經驗缺乏,我們都沒有相關NoSQL 產品的使用經驗。

專案相對來說還比較新。

和以往的儲存相比,資料的關係性操作不再存在。

另附趣圖一張:

詳細分析Memcached快取與Mongodb資料庫的優點與作用

本文詳細講下Memcached和Mongodb一些看法,以及結合應用有什麼好處,希望看到大家的意見和補充。

  Memcached

  Memcached的優勢我覺得總結下來主要體現在:

  1) 分散式。可以由10臺擁有4G記憶體的機器,構成一個40G的記憶體池,如果覺得還不夠大可以增加機器,這樣一個大的記憶體池,完全可以把大部分熱點業務資料儲存進去,由記憶體來阻擋大部分對資料庫讀的請求,對資料庫釋放可觀的壓力。

  2) 單點。如果Web伺服器或App伺服器做負載均衡的話,在各自記憶體中儲存的快取可能各不相同,如果資料需要同步的話,比較麻煩(各自自己過期,還是分發資料同步?),即使資料並不需要同步,使用者也可能因為資料的不一致而產生使用者體驗上的不友好。

  3) 效能強。不用懷疑和資料庫相比確實是,根源上還是記憶體的讀寫和磁碟讀寫效率上幾個數量級的差距。有的時候我們在抱怨資料庫讀寫太差的情況下可以看看磁碟的IO,如果確實是瓶頸的話裝啥強勁的資料庫估計也檔不了,強不強無非是這個資料庫多少充分的利用了記憶體。

  但是也不太建議在任何情況下使用Memcached替代任何快取:

  1) 如果Value特別大,不太適合。因為在預設編譯下Memcached只支援1M的Value(Key的限制到不是最大的問題)。其實從實踐的角度來說也 不建議把非常大的資料儲存在Memcached中,因為有序列化反序列化的過程,別小看它消耗的CPU。說到這個就要提一下,我一直覺得 Memcached適合面向輸出的內容快取,而不是面向處理的資料快取,也就是不太適合把大塊資料放進去拿出來處理之後再放進去,而是適合拿出來就直接給輸出了,或是拿出來不需要處理直接用。

  2) 如果不允許過期,不太適合。Memcached在預設情況下最大30天過期,而且在記憶體達到使用限制後它也會回收最少使用的資料。因此,如果我們要把它當 作static變數的話就要考慮到這個問題,必須有重新初始化資料的過程。其實應該這麼想,既然是快取就是拿到了存起來,如果沒有必定有一個重新獲取重新快取的過程,而不是想著它永遠存在。

  在使用Memcached的過程中當然也會有一些問題或者說最佳實踐:

  1) 清除部分資料的問題。Memcached只是一個Key/Value的池,一個公共汽車誰都可以上。我覺得對於類似的公共資源,如果用的人都按照自己的規 則來的話很容易出現問題。因此,最好在Key值的規範上上使用類似名稱空間的概念, 每一個使用者都能很明確的知道某一塊功能的Key的範圍,或者說字首。帶來的好處是我們如果需要清空的話可以根據這個規範找到我們自己的一批Key然後再去 清空,而不是清空所有的。當然有人是採用版本升級的概念,老的Key就讓它過去吧,到時候自然會清空,這也是一種辦法。不過Key有規範總是有好處的,在 統計上也方便一點。

  2) Value的組織問題。也就是說我們存的資料的粒度,比如要儲存一個列表,是一個儲存在一個鍵值還是統一儲存為一個鍵值,這取決於業務。如果粒度很小的話最好是在獲取的時候能批量獲取,在儲存的時候也能批量儲存。對於跨網路的呼叫次數越少越好,可以想一下,如果一個頁面需要輸出100行資料,每一個數據都需要獲取一次,一個頁面進行上百次連線這個效能會不會成問題。

  那麼Memcached主要用在哪些功能上呢?

  其實我覺得平時能想到在記憶體中做快取的地方我們都可以考慮下是不是可以去適用分散式快取,但是主要的用途還是用來在前端或中部擋一下讀的需求來釋放Web伺服器App伺服器以及DB的壓力。

下面講講Mongodb。

Mongodb

  Mongodb是一款比較優良的非關係型資料庫的文件型的資料庫。它的優勢主要體現在:

  1) 開源。意味著即使我們不去改也可以充分挖掘它,MS SQL除了看那些文件,誰又知道它內部如何實現。

  2) 免費。意味著我們可以在大量垃圾伺服器上裝大量的例項,即使它效能不怎麼高,也架不住非常多的點啊。

  3) 效能高。其它沒比較過,和MS SQL相比,同樣的應用(主要是寫操作)一個撐500使用者就掛了,一個可以撐到2000。在資料量上到百萬之後,即使沒索引,MS SQL的插入效能下降的也一塌糊塗。其實任何事物都有相對性的,在變得複雜變得完善了之後會犧牲一部分的效能,MS SQL體現的是非常強的安全性資料完整性,這點是Mongodb辦不到的。

  4) 配置簡單並且靈活。在生產環境中對資料庫配置故障轉移群集和讀寫分離的資料庫複製是很常見的需求,MS SQL的配置繁瑣的步驟還是很恐怖的,而Mongodb可以在五分鐘之內配置自己所需要的故障轉移組,讀寫分離更是隻需要一分鐘。靈活性體現在,我們可以配置一個M一個S,兩個M一個S(兩個M寫入的資料會合併到S上供讀取),一個M兩個S(一個M寫入的資料在兩個S上有映象),甚至是多個M多個S(理論上可以建立10個M,10個S,我們只需要通過輪詢方式隨便往哪個M上寫,需要讀的時候也可以輪訓任意一個S,當然我們要知道不可能保證在同一時間所有的 S都有一致的資料)。那麼也可以配置兩個M的對作為一套故障轉移群集,然後這樣的群集配置兩套,再對應兩個S,也就是4個M對應2個S,保證M點具有故障 轉移。

  5) 使用靈活。在之前的文章中我提到甚至可以通過SQL到JS表示式的轉換讓Mongodb支援SQL語句的查詢,不管怎麼說Mongodb在查詢上還是很方便的。

  之前也說過了,並不是所有資料庫應用都使用採用Mongodb來替代的,它的主要缺點是:

  1) 開源軟體的特點:更新快,應用工具不完善。由於更新快,我們的客戶端需要隨著它的更新來升級才能享受到一些新功能,更新快也意味著很可能在某一階段會缺乏某個重要功能。另外我們知道MS SQL在DEV/DBA/ADM多個維度都提供了非常好的GUI工具對資料庫進行維護。而Mongodb雖然提供了一些程式,但是並不是非常友好。我們的 DBA可能會很鬱悶,去優化Mongodb的查詢。

  2) 操作事務。Mongodb不支援內建的事務(沒有內建事務不意味著完全不能有事務的功能),對於某些應用也就不適合。不過對於大部分的網際網路應用來說並不存在這個問題。

  在使用Mongodb的過程中主要遇到下面的問題:

  1) 真正的橫向擴充套件?在使用Memcached的過程中我們已經體會到這種爽了,基本可以無限的增加機器來橫向擴充套件,因為什麼,因為我們是通過客戶端來決定鍵值儲存在那個例項上,在獲取的時候也很明確它在哪個例項上,即使是一次性獲取多個鍵值,也是同樣。而對於資料庫來說,我們通過各種各樣的方式進行了 Sharding,不說其它的,在查詢的時候我們根據一定的條件獲取批量的資料,怎麼樣去處理?比如我們按照使用者ID去分片,而查詢根本不在乎使用者ID, 在乎的是使用者的年齡和教育程度,最後按照姓名排序,到哪裡去取這些資料?不管是基於客戶端還是基於服務端的Sharding都是非常難做的,並且即使有了 自動化的Sharding效能不一定能有保障。最簡單的是儘量按照功能來分,再下去就是歷史資料的概念,真正要做到實時資料分散在各個節點,還是很困難。

  2) 多執行緒,多程序。在寫入速度達不到預期的情況下我們多開幾個執行緒同時寫,或者多開幾個Mongodb程序(同一機器),也就是多個數據庫例項,然後向不同 的例項去寫。這樣是否能提高效能?很遺憾,非常有限,甚至可以說根本不能提高。為什麼使用Memcached的時候多開執行緒可以提高寫入速度?那是因為內 存資料交換的瓶頸我們沒達到,而對於磁碟來說,IO的瓶頸每秒那麼幾十兆的是很容易達到的,一旦達到這個瓶頸了,無論是開多少個程序都無法提高效能了。還 好Mongodb使用記憶體對映,看到記憶體使用的多了,其實我對它的信心又多了一點(記憶體佔用多了我覺得CPU更容易讓它不閒著),怕就怕某個DB不使用什 麼記憶體,看著IO瓶頸到了,記憶體和CPU還是吃不飽。

  Memcached和Mongodb的配合

  其實有了Memcached和Mongodb我們甚至可以讓80%以上的應用擺脫傳統關係型資料庫。我能想到它們其實可以互相配合彌補對方的不足:

  Memcached適合根據Key儲存Value,那麼有的時候我們並不知道需要讀取哪些Key怎麼辦呢?我在想是不是可以把Mongodb或 說資料庫當作一個原始資料,這份原始資料中分為需要查詢的欄位(索引欄位)和普通的資料欄位兩部分,把大量的非查詢欄位儲存在Memcached中,小粒 度儲存,在查詢的時候我們查詢資料庫知道要獲取哪些資料,一般查詢頁面也就顯示20-100條吧,然後一次性從Memcached中獲取這些資料。也就是 說,Mongodb的讀的壓力主要是索引欄位,而資料欄位只是在快取失效的時候才有用,使用Memcached擋住大部分實質資料的查詢。反過來說,如果我們要清空Memcached中的資料也知道要清空哪些Key。

MongoDB 文件閱讀筆記 —— 優雅的 NoSQL

NoSQL 資料庫在上年炒得很熱,於是我也萌生了使用 NoSQL 資料庫寫一個應用的想法。首先來認識一下 NoSQL。NoSQL 是一個縮寫,含義從最初的 No-SQL 到現在已經成為了 Not-Only-SQL。確實後面一種解釋比較符合 NoSQL 的使用場景。

現在網路上被人所知的 NoSQL 資料庫可以在這個網頁(http://nosql-database.org)看到。這個列表林林總總一大堆,要選擇哪個資料庫入手呢?

1. 選擇非關係資料庫

在我關注的 Web 領域,特別是 Ruby on Rails 社群,比較多提到的是這幾個資料庫:

Cassandra , apache基金會下的非關係資料庫。早前一段時間傳言 Twitter 要用 Cassandra 替代 Mysql,一時間坊間流傳“NoSQL 要革 SQL 的命了!”。不過Twitter 部落格澄清,Twitter 只是在部分領域使用 Cassandra,存放 Tweets 的主資料庫依然是 MySQL。

      MongoDB10gen 公司的開源非關係資料庫產品,可以選擇他們公司的商業支援。RoR 相關的外掛挺多。

CouchDB,另一個apache基金會下的非關係資料庫。

Redis,特點是執行在記憶體中,速度很快。相比於用來持久化資料,也許更接近於 memcached 這樣的快取系統,或者用來實現任務佇列。(比如resque

在這些候選名單中我選擇了MongoDB。因為它最近在 RoR 社群中的露臉率比較高,網頁文件完善,並且專案主頁的設計也不錯

在陳述 MongoDB 的特性之前,還是給第一次接觸 NoSQL 的人提個醒:不要意圖用 NoSQL 全盤取代 SQL 資料庫。非關係資料庫的出現不是為了取代關係資料庫。具體的說,MongoDB 並不支援複雜的事務,只支援少量的原子操作,所以不適用於“轉帳”等對事務和一致性要求很高的場合。而 MongoDB 適合什麼場合,請繼續閱讀。

2. 文件型資料庫初探

關係資料庫比如 MySQL,通常將不同的資料劃分為一個個“表”,表的資料是按照“行”來儲存的。而關係資料庫的“關係”是指通過“外來鍵”將表間或者表內的資料關聯起來。比如文章-評論 的一對多關係可以用這樣的表來實現:

posts(id, author_id, content, ... )
comments(id, name, email, web_site, content, post_id)
實現關聯的關鍵就是 comments 表的最後一個 post_id 欄位,將 comment 資料的 post_id 欄位設為評論目標文章的 id 值,就可以用 SQL 語句進行相關查詢(假設要查的文章 id 是 1):
SELECT * FROM comments WHERE post_id = 1;
相對於關係資料庫的行式儲存和查詢,MongoDB 作為一個文件型資料庫,可以支援更具層次感的資料。上面舉的 文章-評論 結構,在 MongoDB 裡面可以這樣設計。
{
  _id : ObjectId(...),
  author : 'Rei',
  content : 'content text',
  comments : [ { name : 'Asuka'
                 email : '...',
                 web_site : '...',
                 content : 'comment text'} ]
} 

comments 項是內嵌在 post 項中的(作為陣列)。在 MongoDB 中,一個數據項叫做 Document,一個文件嵌入另一個文件(comment 嵌入 post)叫做 Embed,儲存一系列文件的地方叫做 Collections。順便一提,MongoDB 中也提供類似 SQL 資料庫中的表間關聯,叫做 Reference。 

3. 用文件型資料庫儲存文件

可以看到,文件性資料庫從儲存的資料項上就跟 SQL 資料庫不同。在 MongoDB 中,文件是以 BSON 格式(類似 JSON)儲存的,可以支援豐富的層次的結構。由於資料結構的表達能力更強,用 MongoDB 儲存文件型資料可以比 SQL 資料庫更直觀和高效。

3.1簡化模式設計

在 SQL 資料庫中,為表達資料的從屬關係,通常要將表間關係分為 one-to-one,one-to-many,many-to-many 等模式進行設計,這通常會需要很多連結表的輔助。在MongoDB 中,如果關聯文件體積較小,固定不變,並且與另一文件是主從關係,那麼通常可以嵌入(Embed)主文件。

常見情景:評論、投票點選資料、Tag。

這類場景的有時單個數據項體積很小,但是數量巨大,與之相應的是查詢成本也會上升。如果將這些小資料嵌入所屬文件,在查詢主文件時一併提取,查詢效率要比 SQL 高,後者通常需要開支較大的 JOIN 查詢。並且根據文件介紹,每個文件包括 Embed 部分在物理硬碟上都是儲存在同一區域的,IO 部分也會比 SQL 資料庫快。(注,MongoDB 有單文件大小限制)

3.2動態的文件模式

MongoDB 中的文件其實是沒有模式的,不像 SQL 資料庫那樣在使用前強制定義一個表的每個欄位。這樣可以避免對空欄位的無謂開銷。

例如兩個使用者的聯絡資訊,A 使用者帶有email 不帶 url,B 使用者帶有 url 不帶 email。在 SQL 資料庫中需要為兩個資料項都提供 email 段和 url 段,而在MongoDB 中可以這樣儲存:

[ { name : 'A', email : 'A email address' }, { :name : 'B', url : 'B url address' } ]

在關係資料庫中,如果這些不確定的欄位很多而且數量很大,為了優化考慮可能又要劃分成兩個表了,比如users 和 profiles 兩個表。在 MongoDB 中,如果這一類不確定資訊確實是屬於同一文件的,那麼可以輕鬆的放在一起,因為並不需要預先定義模式,也不會有空欄位的開銷。

不過因為要支援這樣的動態性,並且為了效能考慮進行預先分配硬碟空間,資料外的開銷也會帶來磁碟佔用。我還未實測實際中開銷有多大,也許未來不久我會寫一個應用測試。

4.MongoDB 另外一些特點

4.1JSON 文件式查詢

MongoDB 的查詢語言看起來是這樣的:

>db.users.find( { x : 3, y : "abc" } ).sort({x:1});

這是在 MongoDB 內建的JavaScript 控制檯上的查詢,表示在名為 users 的 Collections 中查詢 x = 3,y = “abc” 的文件,並且以 x 遞增的順序返回資料。

JSON 文件式查詢會讓寫慣應用層程式碼的開發者眼前一亮,但對於精通 SQL 查詢的關係資料庫管理員來說就是一個新的挑戰了。

4.2對分散式的支援

MongoDB 對大型網站的最大吸引力也許來源於其對分散式部署的支援。當今網際網路最流行的資料庫 MySQL 在網站擴大到一定規模之後就會遇到擴充套件瓶頸,解決方案通常是分表分庫、配置主從資料庫。很多網際網路開拓者前仆後繼的為資料庫擴充套件性奮鬥,留下了一頁頁的寶貴經驗。

既然年復一年的有人為同一個問題奮鬥,為什麼不將這些問題在資料庫層面就解決了呢?而MongoDB 的優勢之一就是內建對分散式的支援。

不過我沒有組建資料庫群集的需求,所以還未閱讀這方面的文件。

5. 總結

沒有銀彈,這個教誨已經提過太多。由於缺乏對事務的支援,MongoDB 不太適用於金融等行業的關鍵部分(我想也沒有這個人群來看我部落格吧)。但對於現在要應對海量細資訊的 web 網站來說,MongoDB 可能恰好出現在了正確的時代。NoSQL 的無模式,能讓網站開發的迭代更輕盈;MongoDB 對分散式的支援,可以緩解網站快速成長時在資料庫端的瓶頸疼痛。

不要激進的用 NoSQL 替代所有 MySQL應用,激進的 NoSQL 化只會像5年前的 all rewrite by RoR 浪潮一樣,耗費不必要的精力。但在新專案開發之初,可以考慮是否更適合使用 NoSQL。而我,已經打算在下一個 web 專案裡面使用 MongoDB。

通過 MongoDB 推動 NoSQL(第1部分)

自從 2000 年宣佈 Microsoft.NET Framework 並在 2002 年首次發行以來的過去近十年中,.NET開發人員一直努力適應 Microsoft 推出的各種新事物。但這似乎還不夠,“社群”(包含所有開發人員,無論他們是否每天都使用.NET)也開始行動,創造出更多的新事物來填補Microsoft 未覆蓋到的空白,對您而言這可能是製造混亂和干擾。

在 Microsoft 的支援 範圍之外,該社群所醞釀出的“新”事物之一就是 NoSQL 運動,一組開發人員公開質疑將所有資料儲存於某種形式的關係資料庫系統的這種觀念。表、行、列、主鍵、外來鍵約束、關於null 的爭論以及有關主鍵是否應該為自然鍵或非自然鍵的辯論……還有什麼是神聖不可侵犯的?

在本文及其後續文章中,我將探討NoSQL 運動所倡導的主要工具之一:MongoDB,根據 MongoDB 網站的陳述,該工具的名稱源自於“humongous”(並不是我杜撰的)。我基本上會討論到與 MongoDB 相關的方方面面:安裝、瀏覽以及在.NET Framework 中使用 MongoDB。其中包括其提供的 LINQ 支援;在其他環境(桌面應用程式和Web 應用程式及服務)中使用MongoDB;以及如何設定MongoDB,以免 Windows 生產管理員向您提出嚴重抗議。

問題(或者,為何我要再次關注?)

在深入瞭解MongoDB 之前,讀者自然要問為什麼.NET Framework 開發人員應該犧牲接下來寶貴的大約半小時時間繼續待在電腦前閱讀本文。畢竟,SQLServer 有免費、可再發行的Express Edition,提供比企業或資料中心繫結的傳統關係資料庫更精簡的資料儲存方案,而且毫無疑問,還可以使用大量工具和庫來輕鬆訪問SQL Server 資料庫,其中包括Microsoft 自己的 LINQ 和實體框架。

但問題在於,關係模型(指關係模型本身)的優點也是其最大的缺點。大多數開發人員(無論是.NET、Java 還是其他開發人員都在此列)在經歷短短几年的開發工作之後,就會一一痛訴這種表/行/列的“方正”模型如何不能令其滿意。嘗試對分層資料進行建模的舉動甚至能讓最有經驗的開發人員完全精神崩潰,類似情況不甚列舉,因此Joe Celko 還寫過一本書《SQLfor Smarties, Third Edition》(Morgan-Kaufmann,2005),其中完全是關於在關係模型中對分層資料建模的概念。如果在此基礎之上再增加一個基本前提:關係資料庫認為資料的結構(資料庫架構)不靈活,則嘗試支援資料的臨時“新增”功能將變得十分困難。(快速回答下面的問題:你們之中有多少人處理過包含一個Notes 列(乃至 Note1、Note2、Note3……)的資料庫?)

NoSQL 運動中沒有任何人會說關係模型沒有優點,也沒有人會說關係資料庫將會消失,但過去二十年開發人員生涯的一個最基本的事實是,開發人員經常將資料儲存到本質上並非關係模型(有時甚至與這種模型相去甚遠)的關係資料庫中。

面向文件的資料庫便是用於儲存“文件”(這是一些緊密結合的資料集合,通常並未關聯到系統中的其他資料元素),而非“關係”。例如,部落格系統中的部落格條目彼此毫無關聯,即使出現某一篇部落格確實引用到另一篇部落格的情況,最常用的關聯方法也是通過超連結(旨在由使用者瀏覽器解除引用),而非內部關聯。對本部落格條目的評論完全侷限於本部落格條目的內部範圍,不管評論的是什麼部落格條目,極少有使用者想檢視包含所有評論的內容集合。

此外,面向文件的資料庫往往在高效能或高併發性環境中表現突出:MongoDB專門迎合高效能需求,而它的近親CouchDB 則更多的是針對高併發性的情況。兩者都放棄了對多物件事務的支援,也就是說,儘管它們支援在資料庫中對單個物件進行的併發修改,但若嘗試一次性對多個物件進行修改,將會在一小段時間內看到這些修改正依序進行。文件以“原子方式”更新,但不存在涉及多文件更新的事務概念。這並不意味著MongoDB 沒有任何穩定性,只是說MongoDB 例項與 SQL Server 例項一樣不能經受電源故障。需要原子性、一致性、隔離性和永續性(ACID) 完整要素的系統更適合採用傳統的關係資料庫系統,因此關鍵任務資料很可能不會太快出現在MongoDB 例項內,但Web 伺服器上的複製資料或快取資料可能要除外。

一般來說,若應用程式及元件需要儲存可快速訪問且常用的資料,則採用MongoDB 可以取得較好效果。網站分析、使用者首選項和設定(以及包含非完全結構化資料或需採用結構靈活的資料的任何系統型別)都是採用MongoDB 的自然之選。這並不意味著MongoDB 不能作為操作型資料的主要資料儲存庫;只是說MongoDB 能在傳統 RDBMS 所不擅長的領域內如魚得水,另外它也能在大量其他適合的領域內大展拳腳。

入門

前面提到過,MongoDB是一款開源軟體包,可通過 MongoDB 網站 mongodb.com 輕鬆下載。在瀏覽器中開啟該網站應該就能找到Windows 可下載二進位制包的連結,請在頁面右側查詢“Downloads”連結。另外,如果更願意使用直接連結,請訪問 mongodb.org/display/DOCS/Downloads。截至本文撰寫之時,其穩定版本為發行版1.2.4。它其實就是一個.zip 檔案包,因此相對而言,其安裝過程簡單得可笑:只需在任何想要的位置解壓zip 包的內容。

沒開玩笑,就是這樣。

該 .zip 檔案解壓後生成三個目錄:bin、include 和 lib。唯一有意義的目錄是 bin 目錄,其中包含八個可執行檔案。除此之外不再需要任何其他的二進位制(或執行時)依賴檔案,而事實上,現在只需關注其中的兩個可執行檔案。這兩個檔案分別是mongod.exe(即 MongoDB 資料庫程序本身)和 mongo.exe(即命令列 Shell 客戶端,其使用方法通常類似於傳統的isql.exe SQL Server 命令列Shell 客戶端,用於確保所有內容都已正確安裝且能正常執行,並用於直接瀏覽資料、執行管理任務)。

驗證所有內容是否正確安裝的過程十分簡單,只需在命令列客戶端上啟動mongod。預設情況下,MongoDB將資料儲存在預設的檔案系統路徑c:\data\db,但該路徑是可以配置的,方法是在命令列上通過--config 命令按名稱傳遞一個文字檔案。假設mongod 即將啟動的位置存在一個名為db 的子目錄,驗證所有內容是否安裝得當的過程很簡單,如圖 1 所示。

圖 1 啟動 mongod.exe 以驗證安裝是否成功

如果該目錄不存在,MongoDB並不會建立它。注意在 Windows 7 介面中,當啟動 MongoDB 時,會彈出常見的“該應用程式要開啟埠”對話方塊。請確保能訪問到該埠(預設情況下指27017),或者最多是難以連線到該埠。(在後面一篇文章中,我會討論將MongoDB 投入生產環境,其中將詳細論述到這一問題。)

伺服器進入執行狀態後,通過Shell 連線到該伺服器的過程非常簡單:mongo.exe應用程式啟動一個命令列環境,在該環境中便可直接與伺服器互動,如圖 2 所示。

圖 2 mongo.exe啟動一個命令列環境,用於直接與伺服器互動

預設情況下,Shell連線到“test”資料庫。由於此處目的只是驗證是否一切執行正常,因此使用test 資料庫就夠了。當然,在這裡可以輕鬆地建立一些簡單的示例資料以用於MongoDB,例如建立一個描述某人的快速物件。在MongoDB 中檢視資料的啟動過程非常簡單,如圖 3 所示。

圖 3 建立示例資料

本質上,MongoDB 使用 JavaScript Object Notation (JSON) 作為其資料表示法,這種表示法能表現 MongoDB 的靈活性,並可說明 MongoDB 與客戶端的互動方式。在內部,MongoDB 以 BSON(JSON的二進位制超集)儲存資料,目的是簡化儲存和索引。JSON 保留了MongoDB 的首選輸入/輸出格式,並且通常是在 MongoDB 網站和 wiki 上使用的文件格式。如果不熟悉 JSON,最好是在“深陷”MongoDB 之前充一下電。(開個玩笑。)同時,檢視 mongod 用來儲存資料的目錄,您會發現其中一對以“test”命名的檔案。

言歸正傳,該編寫一些程式碼了。退出Shell 簡單得只需鍵入“exit”,而關閉伺服器也只需在視窗中按 Ctrl+C 或直接關閉視窗:伺服器捕獲到關閉訊號並正確關閉所有內容,然後退出程序。

MongoDB 的伺服器(以及 Shell,儘管它微不足道)是用地道的 C++ 應用程式(還記得嗎?)編寫的,因此訪問該伺服器需要使用某種 .NET Framework 驅動程式,此類驅動程式知道如何通過開啟的套接字進行連線以向伺服器輸送命令和資料。MongoDB 程式包中並未繫結 .NET Framework 驅動程式,但有幸的是,社群提供了一個,此處的“社群”指的是名叫 Sam Corder 的開發人員,他構建了一個 .NET Framework 驅動程式以及 LINQ 支援來訪問 MongoDB。他的作品同時以原始碼形式和二進位制形式提供,位於github.com/samus/mongodb