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

kunlunzhiying發表於2017-12-01

原文地址:http://blog.csdn.net/yiqijinbu/article/details/9053467 


 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風格的資料表現形式,這樣在各種語言中都可以高效地使用。

更多關於BSON的介紹,可以參考:。

寫入協議

Client端訪問Server端使用了輕量級的TCP/IP寫入協議。這種協議在中有詳細介紹,它其實是在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的資料格式非常適合文件化格式的儲存及查詢。

網站資料: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

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

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

用合適的資料庫做適合的事情

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

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

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億級資料量的效能測試

進行了一下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 資料庫可以在這個網頁()看到。這個列表林林總總一大堆,要選擇哪個資料庫入手呢?

1. 選擇非關聯式資料庫

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

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

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

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

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

在這些候選名單中我選擇了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 中,文件是以  格式(類似 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 網站  輕鬆下載。在瀏覽器中開啟該網站應該就能找到Windows 可下載二進位制包的連結,請在頁面右側查詢“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。他的作品同時以原始碼形式和二進位制形式提供,位於。可以從該頁面下載二進位制檔案(查詢頁面右上角),也可以下載原始碼,然後自行編譯。無論採取哪種方式,都會產生兩個程式集:MongoDB.Driver.dll 和 MongoDB.Linq.dll。透過向對應專案的“引用”節點快速新增引用後,就可以使用 .NET Framework 了。

編寫程式碼

從根本上來說,開啟與正在執行的MongoDB 伺服器的連線,同開啟與任何其他資料庫的連線沒有太大差別,如圖 4 所示。

圖 4 開啟與 MongoDB 伺服器的連線

 

using System;
using MongoDB.Driver; 
 
namespace ConsoleApplication1
{
  class Program
  {
    static void Main(string[] args)
    {
      Mongo db = new Mongo();
      db.Connect(); //Connect to localhost on the default port
      db.Disconnect();
    }
  }
}

查詢先前建立的物件並不難,只是與以前.NET Framework 開發人員使用過的方法有所不同而已(請參閱圖 5)。

圖 5 查詢建立的 mongo 物件

 

using System;
using MongoDB.Driver; 
 
namespace ConsoleApplication1
{
  class Program
  {
    static void Main(string[] args)
    {
      Mongo db = new Mongo();
      db.Connect(); //Connect to localhost on the default port.
      Database test = db.getDB("test");
      IMongoCollection things = test.GetCollection("things");
      Document queryDoc = new Document();
      queryDoc.Append("lastname", "Neward");
      Document resultDoc = things.FindOne(queryDoc);
      Console.WriteLine(resultDoc);
      db.Disconnect();
    }
  }
}

如果上述內容看起來太突然,別擔心,寫出這樣的程式碼並非“一日之功”,因為 MongoDB 儲存資料的方式與傳統資料庫是不同的。

對於初學者,請回憶一下,先前插入的資料有三個欄位:firstname、lastname 和 age,這三個元素都可作為資料的檢索條件。但更重要的是,儲存這些資料的行(以強制方式快速完成該過程)為“test.things.save()”,這表示資料被儲存在稱為“things”的事物中。在 MongoDB 術語中,“things”是一個集合,不言而喻,所有資料都儲存在集合中。集合中依次儲存著文件,文件則儲存著“鍵/值”對,而其中的“值”又可以是其他集合。在本例中,“things”就是儲存在前面提到的 test 資料庫內部的集合。

因此,獲取資料的過程首先要連線到MongoDB 伺服器,再連線到test 資料庫,然後查詢集合“things”。這就是圖 5 中前四行的操作:建立一個表示連線的Mongo 物件,連線到伺服器,連線到test 資料庫,然後獲取“things”集合。

返回集合之後,程式碼可透過呼叫FindOne 的方式發出一條查詢命令來查詢單個文件。但與所有資料庫一樣,該客戶端並不想獲取集合中的每一個文件,只想查詢感興趣的文件,因此需要對查詢進行某種方式的約束。在MongoDB 中,該約束的實現方式是建立一個Document,其中包含欄位以及要在這些欄位中搜尋的資料,這是一種稱為示例查詢(簡稱QBE)的概念。由於此處的目標是查詢包含lastname 欄位(其值設為“Neward”)的文件,因此需要建立一個僅包含一個lastname 欄位(及其值)的Document,並作為引數傳遞給FindOne。如果查詢成功,則返回另一個Document,其中包含所有相關資料(外加另一個欄位);否則返回null。

順便提一句,此描述的縮略版可簡化為:

 

Document anotherResult = 
         db["test"]["things"].FindOne(
           new Document().Append("lastname", "Neward"));
       Console.WriteLine(anotherResult);

執行時,不僅會顯示傳入的原始值,還會顯示一個新值,即一個包含ObjectId 物件的 _id 欄位。這是物件的唯一識別符號,是在儲存新資料時由資料庫自動插入的。在嘗試修改此物件時,必須避免修改該欄位,否則資料庫會將該物件視為傳入的新物件。通常,這是透過修改由查詢返回的Document 來完成的:

 

anotherResult["age"] = 39;
       things.Update(resultDoc);
       Console.WriteLine(
         db["test"]["things"].FindOne(
           new Document().Append("lastname", "Neward")));

但是,您始終可以建立新的Document 例項並手動填入_id 欄位來匹配ObjectId(如果這樣做更合理):

 

Document ted = new Document();
       ted["_id"] = new MongoDB.Driver.Oid("4b61494aff75000000002e77");
       ted["firstname"] = "Ted";
       ted["lastname"] = "Neward";
       ted["age"] = 40;
       things.Update(ted);
       Console.WriteLine(
         db["test"]["things"].FindOne(
           new Document().Append("lastname", "Neward")));

當然,如果_id 已知,那麼也可將其用作查詢條件。

請注意,由於Document 被有效地非型別化(無型別),因此幾乎所有內容均能以任意名稱儲存在欄位中,包括某些核心的.NET Framework 值型別,如DateTime。如前所述,從技術角度上講,MongoDB用於儲存 BSON 資料,其中包括傳統 JSON 型別(字串、整數、布林值、雙精度和null,不過 null 僅允許用於物件,不允許用於集合)的某些擴充套件,例如上文提到的ObjectId、二進位制資料、正規表示式以及嵌入式JavaScript 程式碼。我們暫時先不管後面兩種型別,BSON能儲存二進位制資料的這種說法是指能儲存任何可簡化為位元組陣列的內容,這實際上表示MongoDB 能儲存任何內容,但可能無法在該二進位制BLOB 中進行查詢。

關於作者:

Ted Neward 是 Neward & Associates 的負責人,這是一家專門研究 .NET Framework 企業系統和 Java 平臺系統的獨立公司。他曾寫作 100 多篇文章,是 C# 領域最優秀的專家之一併且是 INETA 發言人,著作或合著過十幾本書,包括即將出版的《Professional F# 2.0》(Wrox)。他定期擔任顧問和導師,請透過 ted@tedneward.com 與他聯絡,或透過 blogs.tedneward.com 訪問其部落格。

 

 

透過 MongoDB 推動 NoSQL(第2部分)

 

在,主要介紹了 MongoDB 的基本知識:安裝、執行,以及插入和查詢資料。不過,這篇文章只介紹了基本知識,所用的資料物件是簡單的名稱/值對。這是有道理的,因為 MongoDB 的最大優勢就包括可使用相對簡單的非結構化資料結構。可以肯定地說,這種資料庫能儲存的不只是簡單的名稱/值對。

在本文中,我們將透過一種略微不同的方法來研究MongoDB(或任何技術)。這個稱為探索測試的過程可幫助我們發現伺服器中可能存在的錯誤,同時可以凸顯物件導向開發人員在使用MongoDB 時會遇到的常見問題之一。

前文回顧…

首先,我們要確保討論同樣的問題,還要涉及一些略微不同的新領域。讓我們以一種與前一文章()相比更加結構化的方式來探討MongoDB。我們不只是建立簡單的應用程式,然後進行除錯,我們將採取一舉兩得的做法,建立探索測試。探索測試的程式碼段看起來像單元測試,但它們探索功能而不是嘗試驗證功能。

在研究一項新技術時,編寫探索測試可實現幾種不同的目的。其一,它們有助於發現所研究的技術在本質上是不是可以測試的(假設如下:如果難於進行探索測試,則難於進行單元測試,而這是一個很嚴重的問題)。其二,在所研究的技術出現新的版本時,它們可作為一種迴歸測試,因為它們可在舊功能不再正常工作的情況下發出警告。其三,測試應是相對小型精細的,因此,在本質上,探索測試透過基於以前用例建立新“what-if”用例,使得新技術的學習更為容易。

不過,與單元測試不同,探索測試不是隨應用程式連續開發的,因此,一旦考慮所學習的技術,請將這些測試放在一旁。但不要將它們丟棄,它們還可幫助分離應用程式程式碼中的錯誤與庫或框架中的錯誤。這些測試透過提供一種與應用程式無關的輕型環境來進行實驗,從而完成這種分離,不會產生應用程式開銷。

明確了這一點後,我們來建立Visual C# 測試專案MongoDB-Explore。將MongoDB.Driver.dll 新增到程式集引用列表中,然後進行生成,以確保一切正常。(生成時應選擇作為專案模板的一部分而生成的TestMethod。預設情況下,該測試將會透過,因此一切正常,這意味著,如果專案無法生成,則環境中存在問題。檢查假設是否正確總是很好的方法。)

看起來可以立即著手編寫程式碼了,不過,馬上會出現一個問題:MongoDB需要執行外部伺服器程式(mongod.exe),這樣客戶端程式碼才能對該程式進行連線,執行有用的操作。我們很容易說“好,好,讓我們啟動它,然後開始編寫程式碼”,這樣還是存在一個必然的問題。幾乎可以肯定,15個星期後的某個時候,回頭再看這些程式碼,某些糟糕的開發人員(您、我或團隊同事)會嘗試執行這些測試,看著它們全部失敗,然後浪費兩三天努力尋找原因,這才想起看一看伺服器是否已執行。

經驗教訓:嘗試以某種方式在測試中捕獲所有依賴關係。不管怎樣,問題會再次出現在單元測試過程中。因此,我們需要從全新狀態的伺服器開始,進行一些更改,然後撤消全部更改。要完成這項工作,最簡單的方法是停止並啟動伺服器,現在將問題解決,就為以後節約了時間。

在測試之前(和/或之後)進行執行操作不是什麼新方法,Microsoft測試和實驗室管理器專案可以使用按測試和按測試套件的初始值設定項和清理方法。這些方法包含適用於按測試套件記帳的自定義屬性ClassInitialize 和ClassCleanup 和適用於按測試記帳的TestInitialize 和 TestCleanup。(有關詳細資訊,請參見“”。)因此,按測試套件的初始值設定項將啟動mongod.exe 程式,而按測試套件的清理方法會關閉該程式,如圖 1 所示。

圖 1 測試初始值設定項和清理方法的部分程式碼

 

namespace MongoDB_Explore
{
  [TestClass]
  public class UnitTest1
  {
    private static Process serverProcess;
 
   [ClassInitialize]
   public static void MyClassInitialize(TestContext testContext)
   {
     DirectoryInfo projectRoot = 
       new DirectoryInfo(testContext.TestDir).Parent.Parent;
     var mongodbbindir = 
       projectRoot.Parent.GetDirectories("mongodb-bin")[0];
     var mongod = 
       mongodbbindir.GetFiles("mongod.exe")[0];
 
     var psi = new ProcessStartInfo
     {
       FileName = mongod.FullName,
       Arguments = "--config mongo.config",
       WorkingDirectory = mongodbbindir.FullName
     };
 
     serverProcess = Process.Start(psi);
   }
   [ClassCleanup]
   public static void MyClassCleanup()
   {
     serverProcess.CloseMainWindow();
     serverProcess.WaitForExit(5 * 1000);
     if (!serverProcess.HasExited)
       serverProcess.Kill();
  }
...

上述程式碼第一次執行時,將彈出一個對話方塊,通知使用者正在啟動程式。單擊“確定”,該對話方塊就會消失 ... 直到下一次執行該測試。如果不希望顯示該對話方塊,請找到並選中單選框“不再顯示此對話方塊”,以便不再顯示該訊息。如果正在執行防火牆軟體(如Windows 防火牆),也可能出現該對話方塊,這是因為伺服器需要開啟一個埠來接收客戶端連線。採用同樣的方法處理,所有操作都應以無提示方式執行。如果需要,可在清理程式碼的第一行放置一個斷點,驗證伺服器是否正在執行。

只要伺服器正在執行,就可開始測試,除非出現另一個問題:每個測試都需要使用自己的全新資料庫,但資料庫中預先存在一些資料也是很有用的,這樣,更便於進行某些方面(如查詢)的測試。每個測試最好都有自己的預先存在的全新資料。包含TestInitializer 和 TestCleanup的方法可以完成這一任務。

對此加以討論之前,我們來看一看這個快速TestMethod,它嘗試確保找到伺服器,進行連線,插入、找到和刪除物件,使探索測試的速度提高到前一文章所介紹的那樣(請參見圖 2)。

圖 2 TestMethod確保找到伺服器並進行連線

 

[TestMethod]
public void ConnectInsertAndRemove()
{
  Mongo db = new Mongo();
  db.Connect();
 
  Document ted = new Document();
  ted["firstname"] = "Ted";
  ted["lastname"] = "Neward";
  ted["age"] = 39;
  ted["birthday"] = new DateTime(1971, 2, 7);
  db["exploretests"]["readwrites"].Insert(ted);
  Assert.IsNotNull(ted["_id"]);
 
  Document result =
    db["exploretests"]["readwrites"].FindOne(
    new Document().Append("lastname", "Neward"));
  Assert.AreEqual(ted["firstname"], result["firstname"]);
  Assert.AreEqual(ted["lastname"], result["lastname"]);
  Assert.AreEqual(ted["age"], result["age"]);
  Assert.AreEqual(ted["birthday"], result["birthday"]);
 
  db.Disconnect();
}

如果執行上述程式碼,執行到宣告時,測試將失敗。具體來說,問題出在最後一條關於“birthday”的宣告。很顯然,若將 DateTime 傳送到沒有時間的 MongoDB 資料庫中,是不會正確往返的。進入的資料型別是關聯時間為午夜的日期,返回的是關聯時間為早上8 點的日期,這不符合測試末尾處的AreEqual 宣告。

這一點凸顯出探索測試的用處,要是不使用探索測試(舉例來說,前一文章中的程式碼就是這樣),可能要到專案進行幾個星期或幾個月後才會注意到MongoDB 的這一小特性。這是不是MongoDB 伺服器中的錯誤是一種價值判斷,不需要馬上探討。重要的是,探索測試對技術進行放大觀察,有助於隔離這種“有趣的”行為。因此,希望使用該技術的開發人員可以確定這是不是一個重要更改。有備無患。

順便提一下,若要修復這段程式碼從而透過測試,需要將從資料庫返回的DateTime 轉換為本地時間。我曾在一個線上論壇中提出這個問題,MongoDB.Driver的作者 Sam Corder 的回答是:“所有進入的日期都會轉換為 UTC,並返回 UTC 時間。”因此,必須將 DateTime 轉換為 UTC 時間才能透過DateTime.ToUniversalTime 進行儲存,或者透過DateTime.ToLocalTime 將從資料庫檢索的所有DateTime 轉換為本地時區,示例程式碼如下:

 

Assert.AreEqual(ted["birthday"], 
  ((DateTime)result["birthday"]).ToLocalTime());

這件事本身凸顯了社群的一個極大的優點,即通訊雙方的距離就是一封電子郵件。

增加複雜性

希望使用 MongoDB 的開發人員需要知道,與最初給人的印象相反,它不是一個物件資料庫,也就是說,如果得不到幫助,它無法任意處理複雜物件圖。一些常規做法可以提供這種幫助,不過迄今為止,還是需要開發人員才能實現。

例如,考慮圖 3 所示的簡單物件集合,該集合用於反映很多文件的儲存情況,而這些文件描述的是一個有名的家庭。至此不會有什麼問題。實際上,執行測試時,測試應向資料庫查詢插入的物件(如圖 4 所示),這是為了確保這些物件是可以檢索的。這樣,測試透過。真是太妙了。

圖 3 簡單物件集合

 

[TestMethod]
public void StoreAndCountFamily()
{
  Mongo db = new Mongo();
  db.Connect();
 
  var peter = new Document();
  peter["firstname"] = "Peter";
  peter["lastname"] = "Griffin";
 
  var lois = new Document();
  lois["firstname"] = "Lois";
  lois["lastname"] = "Griffin";
 
  var cast = new[] {peter, lois};
  db["exploretests"]["familyguy"].Insert(cast);
  Assert.IsNotNull(peter["_id"]);
  Assert.IsNotNull(lois["_id"]);
 
  db.Disconnect();
}

圖 4 向資料庫查詢物件

 

[TestMethod]
public void StoreAndCountFamily()
{
  Mongo db = new Mongo();
  db.Connect();
 
  var peter = new Document();
  peter["firstname"] = "Peter";
  peter["lastname"] = "Griffin";
 
  var lois = new Document();
  lois["firstname"] = "Lois";
  lois["lastname"] = "Griffin";
 
  var cast = new[] {peter, lois};
  db["exploretests"]["familyguy"].Insert(cast);
  Assert.IsNotNull(peter["_id"]);
  Assert.IsNotNull(lois["_id"]);
 
  ICursor griffins =
    db["exploretests"]["familyguy"].Find(
      new Document().Append("lastname", "Griffin"));
  int count = 0;
  foreach (var d in griffins.Documents) count++;
  Assert.AreEqual(2, count);
 
  db.Disconnect();
}

實際上,這種情況可能不完全是真實的。細緻的讀者如果鍵入程式碼就可能發現,說到底,測試並沒有透過,因為預期的物件數與2 不匹配。這是因為,正如通常的資料庫一樣,這個資料庫的狀態在多次呼叫中保持不變,此外,由於測試程式碼不顯式刪除物件,這些物件在各個測試中都存在。

這凸顯了面向文件資料庫的另外一個特點:完全可能存在重複項,也允許存在重複項。正因為這,每個文件一經插入,都會由implicit_id 屬性進行標記,並且有一個唯一的識別符號儲存在該屬性中,這個唯一識別符號實際上會成為文件的主鍵。

因此,如果要透過測試,需要在執行每個測試之前清除資料庫。儘管刪除MongoDB 儲存檔案的目錄中的檔案十分容易,但最好使測試套件能夠自動執行這一任務。每個測試都可在完成後以手動方式完成這一任務,時間一長,這會變得有些乏味。測試程式碼可利用Microsoft 測試和實驗室管理器的TestInitialize 和 TestCleanup功能來捕獲常用程式碼(何不包括資料庫連線和斷開邏輯),如圖 5 所示。

圖 5 利用 TestInitialize 和TestCleanup

 

private Mongo db;
 
[TestInitialize]
public void DatabaseConnect()
{
  db = new Mongo();
  db.Connect();
}
        
[TestCleanup]
public void CleanDatabase()
{
  db["exploretests"].MetaData.DropDatabase();
 
  db.Disconnect();
  db = null;
}

CleanDatabase 方法的最後一行不是必不可少的,因為下一個測試會用新的 Mongo 物件覆蓋該欄位引用,不過,有時最好明確表示出該引用不再有內容。用者自慎。重要的是刪除在測試中使用過的資料庫,清空MongoDB 用於儲存資料的檔案,一切都以全新的狀態迎接下一個測試。

不過,就目前情況看,該家庭模型是不完整的。所引用的兩個人是一對伴侶,假設他們應將對方引用為配偶,如下所示:

 

peter["spouse"] = lois;
  lois["spouse"] = peter;

如果在測試中執行這段程式碼,會產生StackOverflowException。MongoDB驅動程式序列化程式本身不理解迴圈引用的概念,它會無休止地引用下去。天哪。這可不是什麼好事。

若要修復這一問題,可以在兩種方法中選擇其一。一種方法是,配偶欄位可使用其他文件的_id 欄位來填充(該文件插入後)和更新,如圖 6 所示。

圖 6 解決迴圈引用問題

 

[TestMethod]
public void StoreAndCountFamily()
{
  var peter = new Document();
  peter["firstname"] = "Peter";
  peter["lastname"] = "Griffin";
 
  var lois = new Document();
  lois["firstname"] = "Lois";
  lois["lastname"] = "Griffin";
 
  var cast = new[] {peter, lois};
  var fg = db["exploretests"]["familyguy"];
  fg.Insert(cast);
  Assert.IsNotNull(peter["_id"]);
  Assert.IsNotNull(lois["_id"]);
 
  peter["spouse"] = lois["_id"];
  fg.Update(peter);
  lois["spouse"] = peter["_id"];
  fg.Update(lois);
 
  Assert.AreEqual(peter["spouse"], lois["_id"]);
  TestContext.WriteLine("peter: {0}", peter.ToString());
  TestContext.WriteLine("lois: {0}", lois.ToString());
  Assert.AreEqual(
    fg.FindOne(new Document().Append("_id",
    peter["spouse"])).ToString(),
    lois.ToString());
 
  ICursor griffins =
    fg.Find(new Document().Append("lastname", "Griffin"));
  int count = 0;
  foreach (var d in griffins.Documents) count++;
  Assert.AreEqual(2, count);
}

不過,這種方法有一個缺點:它要求將文件插入資料庫,並根據需要將它們的_id 值(在MongoDB.Driver 中是 Oid 例項)複製到每個物件的配偶欄位中。這時,每個文件會再次更新。儘管訪問MongoDB 資料庫與傳統RDBMS 更新相比速度是很快的,這種方法仍有些費時。

第二種方法是為每個文件預先生成Oid 值,填充配偶欄位,然後將整個批次傳送到資料庫,如圖 7 所示。

圖 7 一種更好的解決迴圈引用問題的方法

 

[TestMethod]
public void StoreAndCountFamilyWithOid()
{
  var peter = new Document();
  peter["firstname"] = "Peter";
  peter["lastname"] = "Griffin";
  peter["_id"] = Oid.NewOid();
 
  var lois = new Document();
  lois["firstname"] = "Lois";
  lois["lastname"] = "Griffin";
  lois["_id"] = Oid.NewOid();
 
  peter["spouse"] = lois["_id"];
  lois["spouse"] = peter["_id"];
 
  var cast = new[] { peter, lois };
  var fg = db["exploretests"]["familyguy"];
  fg.Insert(cast);
 
  Assert.AreEqual(peter["spouse"], lois["_id"]);
  Assert.AreEqual(
    fg.FindOne(new Document().Append("_id",
    peter["spouse"])).ToString(),
    lois.ToString());
 
  Assert.AreEqual(2, 
    fg.Count(new Document().Append("lastname", "Griffin")));
}

這種方法僅需要Insert 方法,因為Oid 值是提前已知的。順便提請注意,對宣告測試的ToString 呼叫是特意進行的,這樣,文件會在進行比較之前轉換為字串。

圖 7 的程式碼中,真正務必要注意的是,對透過Oid 引用的文件解除引用可能比較困難和乏味,因為面向文件這種形式假設文件或多或少是獨立實體或分層實體,而不是物件圖。(請注意,.NET驅動程式提供了 DBRef,後者可透過略微更豐富的方式來引用/解除引用其他文件,但仍無法實現物件圖友好的系統。)因此,儘管肯定可以獲得一個豐富的物件模型並將其儲存到MongoDB 資料庫中,仍不建議這樣做。請堅持使用Word 或 Excel 這樣的文件來儲存緊密群集的資料組。如果某些內容可視為大型文件或電子表格,則可能非常適合MongoDB 或其他某種面向文件的資料庫。

 

 

透過 MongoDB 推動 NoSQL(第3部分)

上一次,我使用探索測試繼續對MongoDB 進行探討。我介紹瞭如何在測試期間啟動和停止伺服器,然後介紹瞭如何獲取跨文件引用,並探討了導致如此麻煩舉動的原因。現在我們需要探索更多中間的 MongoDB 功能:謂詞查詢、聚合函式以及 MongoDB.Linq 程式集提供的 LINQ 支援。我還將提供一些有關在生產環境中安置 MongoDB 的注意事項。

當我們最終離開我們的主角 .. .

在相關的程式碼包中,我充實了探索測試的內容,使用我最喜歡的電視節目裡的人物,加入了一組已存在的示例資料集以供處理。圖 1 顯示了之前的探索測試,已經過重新整理器處理。到目前為止一切順利。

圖 1 示例探索測試

 

[TestMethod]
        public void StoreAndCountFamilyWithOid()
        {
          var oidGen = new OidGenerator();
          var peter = new Document();
          peter["firstname"] = "Peter";
          peter["lastname"] = "Griffin";
          peter["_id"] = oidGen.Generate();
 
          var lois = new Document();
          lois["firstname"] = "Lois";
          lois["lastname"] = "Griffin";
          lois["_id"] = oidGen.Generate();
 
          peter["spouse"] = lois["_id"];
          lois["spouse"] = peter["_id"];
 
          var cast = new[] { peter, lois };
          var fg = db["exploretests"]["familyguy"];
          fg.Insert(cast);
 
          Assert.AreEqual(peter["spouse"], lois["_id"]);
          Assert.AreEqual(
            fg.FindOne(new Document().Append("_id",
              peter["spouse"])).ToString(), lois.ToString());
 
          Assert.AreEqual(2,
            fg.Count(new Document().Append("lastname", "Griffin")));
        }

呼叫所有老人 .. .

在前面的文章中,客戶端程式碼已經獲得所有匹配特定標準的文件(例如“lastname”欄位匹配特定的 String 或“_id”欄位匹配特定的 Oid),但我還沒有介紹如何進行謂詞查詢(例如“找到所有‘age’欄位的值大於 18 的文件”)。事實上,MongoDB 並不使用 SQL 風格的介面來描述要執行的查詢,而是使用ECMAScript/JavaScript,而且它能接受要在伺服器上執行的程式碼塊以篩選或聚合資料,幾乎就像儲存過程一樣。

這提供了一些類似LINQ 的功能,甚至不用瞭解Mongo.Linq 程式集支援的LINQ 功能我們就能看出來。透過指定包含名為“$where”的欄位的文件和描述要執行的ECMAScript 程式碼的程式碼片段,可以隨意建立複雜的查詢:

 

[TestMethod]
        public void Where()
        {
          ICursor oldFolks =
            db["exploretests"]["familyguy"].Find(
            new Document().Append("$where", 
            new Code("this.gender === 'F'")));
          bool found = false;
          foreach (var d in oldFolks.Documents)
            found = true;
          Assert.IsTrue(found, "Found people");
        }

正如您所看到的,Find呼叫返回 ICursor 例項,儘管該例項本身不是 IEnumerable(表示它無法用在 ForEach 迴圈中),卻包含一個型別為IEnumerable<Document> 的Documents 屬性。如果查詢會返回很大的資料集,透過將其Limit 屬性設定為 n,可以要求 ICursor 返回前 n 個結果。

謂詞查詢的語法共有四種格式,如圖 2 所示。

圖 2 四種不同的謂詞查詢語法

 

[TestMethod]
        public void PredicateQuery()
        {
          ICursor oldFolks =
            db["exploretests"]["familyguy"].Find(
            new Document().Append("age",
            new Document().Append("$gt", 18)));
          Assert.AreEqual(6, CountDocuments(oldFolks));
 
          oldFolks =
            db["exploretests"]["familyguy"].Find(
            new Document().Append("$where",
            new Code("this.age > 18")));
          Assert.AreEqual(6, CountDocuments(oldFolks));
 
          oldFolks =
            db["exploretests"]["familyguy"].Find("this.age > 18");
          Assert.AreEqual(6, CountDocuments(oldFolks));
 
          oldFolks =
            db["exploretests"]["familyguy"].Find(
            new Document().Append("$where",
            new Code("function(x) { return this.age > 18; }")));
          Assert.AreEqual(6, CountDocuments(oldFolks));
        }

在第二種和第三種格式中,“this”指的是要查詢的物件。

事實上,您可以使用文件傳達查詢或命令,以便從驅動程式向資料庫傳送任意命令(即ECMAScript 程式碼)。例如,IMongoCollection介面提供的 Count 方法就是對這段冗長程式碼的簡便替代方式:

 

[TestMethod]
        public void CountGriffins()
        {
          var resultDoc = db["exploretests"].SendCommand(
            new Document()
              .Append("count", "familyguy")
              .Append("query",
                new Document().Append("lastname", "Griffin"))
            );
          Assert.AreEqual(6, (double)resultDoc["n"]);
        }

這意味著 MongoDB 文件介紹的所有聚合操作(例如“distinct”或“group”)都可以透過同一種機制進行訪問,雖然MongoDB.Driver API 未將它們作為方法提供。

您可以透過“特殊名稱”語法“$eval”將查詢之外的任意命令傳送到資料庫,這樣就可以對伺服器執行任何合法的ECMAScript 程式碼塊,仍舊很像儲存過程:

 

[TestMethod]
        public void UseDatabaseAsCalculator()
        {
          var resultDoc = db["exploretests"].SendCommand(
            new Document()
              .Append("$eval", 
                new CodeWScope { 
                  Value = "function() { return 3 + 3; }", 
                  Scope = new Document() }));
          TestContext.WriteLine("eval returned {0}", resultDoc.ToString());
          Assert.AreEqual(6, (double)resultDoc["retval"]);
        }

或者直接對資料庫使用所提供的Eval 函式。如果這還不夠靈活,MongoDB允許在特殊的資料庫集合“system.js”中新增 ECMAScript 函式,從而在資料庫例項上儲存使用者定義的要在查詢時執行的ECMAScript 函式以及伺服器端執行塊,如 所述。

缺少的 LINQ

C# MongoDB 驅動程式也有LINQ 支援,允許開發人員編寫如圖 3 所示的 MongoDB 客戶端程式碼。

圖 3 LINQ支援示例

 

[TestMethod]
        public void LINQQuery()
        {
          var fg = db["exploretests"]["familyguy"];
          var results = 
            from d in fg.Linq() 
            where ((string)d["lastname"]) == "Brown" 
            select d;
          bool found = false;
          foreach (var d in results)
          {
            found = true;
            TestContext.WriteLine("Found {0}", d);
          }
          Assert.IsTrue(found, "No Browns found?");
        }

而且為了保持MongoDB 資料庫的動態特徵,此示例不需要生成程式碼,只需呼叫Linq 以返回可以“啟用”MongoDBLINQ 提供程式的物件即可。在我撰寫本文時,LINQ支援還相當粗略,但在本文發表時,它將得到極大改進。新功能的文件和相關示例將在 欄目釋出。

釋出是一項功能

最重要的是,如果要將MongoDB 用在生產環境中,對於那些要讓生產伺服器和服務保持執行的工作人員來說,還有幾個問題需要解決,這樣才能減輕他們的工作負擔。

首先,需要將伺服器程式(mongod.exe) 安裝為服務,因為在生產伺服器上一般不允許在互動式桌面會話中執行該程式。因此,mongod.exe支援一個服務安裝選項“--install”,透過該選項可將其安裝為服務,然後透過服務皮膚或命令列“netstart MongoDB”啟動。但是,截止本文撰寫時,--install命令存在一個小問題:它透過檢視執行安裝所用的命令列來推斷可執行檔案的路徑,因此必須在命令列中指定完整路徑。也就是說,如果MongoDB 安裝在C:\Prg\mongodb 中,您必須在命令提示符處(使用管理員許可權)使用命令C:\Prg\mongodb\bin\mongod.exe --install 將其安裝為服務。

但是,所有的命令列引數,例如“--dbpath”,必須也顯示在該安裝命令中,這意味著如果埠、資料檔案的路徑等等設定發生更改,則必須重新安裝服務。幸運的是,MongoDB支援一個配置檔案選項(透過“--config”命令列選項指定),因此通常最好的做法是將配置檔案的完整路徑傳遞到服務安裝命令,然後在檔案中進行其他所有配置:

 

C:\Prg\mongodb\bin\mongod.exe --config C:\Prg\mongodb\bin\mongo.cfg --install
net start MongoDB

像往常一樣,測試服務是否成功執行的最簡便方法就是使用MongoDB 下載隨附的mongo.exe 客戶端來連線服務。由於伺服器透過套接字與客戶端通訊,因此您需要在防火牆中設定通道,以便允許與伺服器的通訊。

沒有您需要的資料機器人

當然,對 MongoDB 伺服器的不安全訪問不可能是什麼好事,因此阻止不需要的訪問者訪問伺服器成為一項重要功能。MongoDB支援身份驗證,但是它的安全系統不像SQL Server 這樣的“大塊頭”資料庫那麼精密。

一般來說,第一步是使用mongo.exe 客戶端連線資料庫並將管理員使用者新增到管理資料庫中(該資料庫中包含用於執行和管理整個MongoDB 伺服器的資料),從而建立資料庫管理登入,如下所示:

 

> use admin
> db.addUser("dba", "dbapassword")

完成之後,所有進一步的操作(甚至是該外殼程式中的操作)都需要經過身份驗證,透過在該外殼程式中進行顯式身份驗證完成:

 

> db.authenticate("dba", "dbapassword")

DBA 現在可以更改資料庫並使用前面所示的同一個addUser 呼叫來新增使用者,從而將使用者新增到MongoDB 資料庫中:

 

> use mydatabase
> db.addUser("billg", "password")

透過Mongo.Driver 連線資料庫時,身份驗證資訊將作為用於建立Mongo 物件的連線字串的一部分進行傳遞,相同的身份驗證過程將透明地進行:

 

var mongo = new Mongo("Username=billg;Password=password");

很自然,密碼不應該直接硬編碼在程式碼中或公開儲存,而應該使用與所有基於資料庫的應用程式相同的密碼規則。實際上,整個配置(主機、埠、密碼等)應該儲存在配置檔案中並透過ConfigurationManager 類進行檢索。

擴充套件到另一些程式碼

管理員應該定期檢視正在執行的例項,以獲得正在執行的伺服器的相關診斷資訊。MongoDB支援一個 HTTP 介面用於與資料庫互動,該介面執行的埠號比用於普通客戶端通訊的埠號高1,000。MongoDB 的預設埠是 27017,因此該 HTTP 介面位於埠 28017,如圖 4 所示。

圖 4 用於與 MongoDB 互動的HTTP 介面

該 HTTP 介面還允許更加偏向 REST 風格的通訊方法,與MongoDB.Driver 和MongoDB.Linq 中的本機驅動程式正相反;MongoDB網站對此有詳細介紹,但用於訪問集合內容的HTTP URL 中需要新增資料庫名稱和集合名稱(用斜線分隔),如圖 5 所示。

圖 5 用於訪問集合內容的 HTTP URL

有關使用 WCF 建立 REST 客戶端的詳細資訊,請參見 MSDN 文章“”。

專家的忠告

MongoDB 是一款發展迅速的產品,這些旨在探索MongoDB 核心功能的文章有很多內容並未涉獵。儘管MongoDB 不能直接替代SQL Server,但在傳統的關聯式資料庫管理系統作用有限的領域,它確實是一種可行的儲存替代方案。與MongoDB 相同,mongodb-csharp專案也處在蓬勃發展之中。撰寫本文時,很多新的改進功能已經新增到Beta 版中,包括使用普通物件處理強型別化的集合以及對LINQ 支援的重大改進。請密切注意這兩個專案。

但現在我們也應該和MongoDB 說再見了。讓我們把注意力轉向孜孜不倦的程式設計師可能不熟悉(也應該存在爭議)的其他開發人員領域。但在目前,愉快程式設計,並記住偉大的開發專家曾說過的“開發人員使用原始碼獲取知識,進行防禦,切勿成為駭客”。

關於作者:

Ted Neward 是 Neward & Associates 的負責人,這是一家專門研究 .NET Framework 企業系統和 Java 平臺系統的獨立公司。他曾寫作 100 多篇文章,是 C# 領域最優秀的專家之一併且是 INETA 發言人,著作或合著過十幾本書,包括即將出版的《Professional F# 2.0》(Wrox)。他定期擔任顧問和導師,請透過 ted@tedneward.com 與他聯絡,或透過 blogs.tedneward.com 訪問其部落格。

 

MongoDB之父:MongoDB勝過BigTable

 

Dwight Merriman和他的團隊,包括ShopWiki的創始人Eliot Horowitz參加了在紐約10gen啟動MongoDB的儀式。現在該公司除了擔任該開源專案的主要運營者之外,還提供支援、培訓和諮詢服務。10gen在舊金山舉辦了第二屆開發者大會,Merriman在上午的大會做了主題演講,主要介紹了MongoDB的起源,並解釋了為何要建立這樣的資料庫。

“在2007年底,當時的想法是構建一個用於開發、託管並具有自動縮放Web應用程式的線上服務”,談到MongoDB誕生之目的時,Merriman介紹道。“但是不同於Google App Engine的是,這項服務完全建立在一個開放原始碼的軟體平臺之上。”因此,在關注了Google Bigtable架構很長一段時間後,Merriman和他的團隊注意到,尚沒有一個開源的資料庫平臺適合這種服務,這興許是個機會。

“我們意識到很多現有的資料庫並不真正具備‘雲端計算’的特性。例如彈性、可擴充套件性以及易管理性。這些特效能夠為開發者和運營者帶來便利,而MySQL還不完全具備這些特點。

因此,Dwight Merriman以及他的團隊的目標是構建一個全新的資料庫。新的資料庫將會放棄大家所熟悉的關聯式資料庫模型,且是適合現代網路應用並基於分散式的平臺。高度事務性的系統可以幫助解決一些棘手的問題,同時還支援雲端計算架構的伸縮性。Merriman解釋到。經過一年的不斷努力,這個資料庫已經比較完善。他們將它設計為具有為“雲端計算服務”潛力的資料庫。而且還會不斷的完善,因為MongoDB本身就是一個開源資料庫。

在開源的、面向文件的資料庫中,MongoDB經常被譽為具有RDBMS功能的NoSQL資料庫。MongoDB還帶有互動式shell,這使得訪問其資料儲存變得簡單,且其對於分塊的即裝即用的支援能夠使高可伸縮性跨多個節點。

據悉,MongoDB的API是JSON物件和JavaScript函式的本地混合物。透過shell程式開發人員可與MongoDB進行互動,即允許命令列引數,或透過使用語言驅動程式來訪問資料儲存例項。這裡不存在類JDBC驅動程式,這意味著開發人員不必處理ResultSet或PreparedStatement。

而速度是 MongoDB 的另外一個優勢,主要是由於它處理寫入的方式:它們儲存在記憶體中,然後透過後臺執行緒寫入磁碟。

“由於使用者不容易在大規模環境下作分散式的連結,並且在分散式環境下很難做快速的大規模部署,因此,使用者需要一些輔助的東西”,Memmiman解釋道。

最後他表示同樣重要的是為了限制資料庫的事務語義你可以使用分散式事務。但當你在1000臺機器上執行時它不會那麼快。例如銀行或會計系統。傳統的關係型資料庫目前還是更適用於需要大量原子性複雜事務的應用程式。(李智/譯)

 

 

MongoDB與CouchDB全方位對比

本文見於MongoDB官方網站,MongoDB與CouchDB很相似,他們都是文件型儲存,資料儲存格式都是JSON型的,都使用Javascript進行操作,都支援Map/Reduce。但是其實二者有著很多本質的區別,本文透過現象追尋本質,讓你更好的理解MongoDB與CouchDB。

1.MVCC(Multiversionconcurrency control)

MongoDB與CouchDB的一大區別就是CouchDB是一個MVCC的系統,而MongoDB是一個update-in-place的系統。這二者的區別就是,MongoDB進行寫操作時都是即時完成寫操作,寫操作成功則資料就寫成功了,而CouchDB一個支援多版本控制的系統,此類系統通常支援多個結點寫,而系統會檢測到多個系統的寫操作之間的衝突並以一定的演算法規則予以解決。

2.水平擴充套件性

在擴充套件性方面,CouchDB使用replication去做,而MongoDB的replication僅僅用來增強資料的可靠性,MongoDB在實現水平擴充套件性方面使用的是Sharding。(據說CouchDB也有開發分片功能的計劃)

3.資料查詢操作

這個區別在使用者介面上了,MongoDB與傳統的資料庫系統類似,支援動態查詢,即使在沒有建立索引的行上,也能進行任意的查詢。而CouchDB不同,CouchDB不支援動態查詢,你必須為你的每一個查詢模式建立相應的,並在此view的基礎上進行查詢。

4.原子性

這一點上兩者比較一致,都支援針對行的原子性修改(concurrentmodifications of single documents),但不支援更多的複雜事務操作。

5.資料可靠性

CouchDB是一個”crash-only”的系統,你可以在任何時候停掉CouchDB並能保證資料的一致性。而MongoDB在不正常的停掉後需要運repairDatabase()命令來修復資料檔案,在1.7.5版本後支援單機可靠的–dur命令。

6.Map/Reduce

MongoDB和CouchDB都支援Map/Reduce,不同的是MongoDB只有在資料統計操作中會用到,而CouchDB在變通查詢時也是使用Map/Reduce。

7.使用 javascript

MongoDB和CouchDB都支援javascript,CouchDb用javascript來建立view。MongoDB使用JSON作為普通資料庫操作的表示式。當然你也可以在操作中包含。MongoDB還支援服務端的javascript指令碼(running arbitrary javascript functions server-side),當然,MongoDB的Map/Reduce函式也是javascript格式的。

8.REST

CouchDB是一個RESTFul的資料庫,其操作完全走HTTP協議,而MongoDB是走的自己的二進位制協議。MongoDB Server在啟動時可以開放一個HTTP的介面供狀態監控。

9.效能

此處主要列舉了MongoDB自己具有高效能的原因

採用二進位制協議,而非CouchDB REST的HTTP協議

使用Momary Map記憶體對映的做法

collection-oriented,面向集合的儲存,同一個collection的資料是連續儲存的

update-in-place直接修改,而非使用MVCC的機制

使用C++編寫

10.適用場景

如果你在構建一個 Lotus Notes型的應用,我們推薦使用CouchDB,主要是由於它的MVCC機制。另外如果我們需要master-master的架構,需要基於地理位置的資料分佈,或者在資料結點可能不線上的情況下,我們推薦使用CouchDB。

如果你需要高效能的儲存服務,那我們推薦MongoDB,比如用於儲存大型網站的使用者個人資訊,比如用於構建在其它儲存層之上的Cache層。

如果你的需求中有大量update操作,那麼使用MongoDB吧。就像我們在例子updating real time analytics counters中的一樣,對於那種經常變化的資料,比如瀏覽量,訪問數之類的資料儲存。

 

Mongodb GridFS 介紹

MongoDB GridFS 是MongoDB用於檔案儲存的模組,本文簡單介紹了期用法。

mongodb GridFS 效能

效能, 網評還不錯.

不過在生產環境中,國外有用於儲存影片流的.

GridFS的一個優點是可以儲存上百萬的檔案而無需擔心擴容性.

透過同步複製,可以解決分散式檔案的備份問題.

透過ARP-ping可以實現一個雙機熱備切換,類mysql的mysql master master replic

使用Nginx module

這是gridfs的nginx module. 可以透過nginx直接訪問讀取mongo gridfs中的檔案.

和nginx對應的mogilefs module類似.

優點: 由於直接透過nginx,速度是最快的.

缺點: 只能透過file_path來查詢,目前不支援_id來查詢.因此必須在file_path上

建立索引.

其他一些資訊:

1.透過runcommand可以直接在mongodb端執行處理指令碼. 比如像mapreduce,或者一

些需要讀取資料然後進行處理的.

這些command則是使用javascript方式來編寫的,很容易. 好處就是避免了資料在服

務端和客戶端之間的讀取和傳輸,

提高效率.

2. sharding

sharding在目前開發版中已經具備,但還不成熟. 但是可以自己實現sharding比較

好.因為目前的sharding還是比較硬性的.

3.靈活使用magic運算子和upsert,比如$inc,$all,$in 等等

 

MongoDB:下一代MySQL?

MongoDB的特性

  • 簡單的查詢語句,沒有Join操作
  • 文件型儲存,其資料是用二進位制的Json格式Bson儲存的。其資料就像Ruby的hashes,或者Python的字典,或者PHP的陣列
  • Sharding,MongoDB提供auto-sharding實現資料的擴充套件性
  • GridFS,MongoDB的提供的檔案儲存API
  • 陣列索引,你可以對文件中的某個陣列屬性建立索引
  • MapReduce,可以用於進行復雜的統計和平行計算
  • 高效能,透過使用mmap和定時fsync的方法,避免了頻繁IO,使其效能更高

MongoDB的優點

  • 高效能,速度非常快(如果你的記憶體足夠的話)
  • 沒有固定的表結構,不用為了修改表結構而進行資料遷移
  • 查詢語言簡單,容易上手
  • 使用Sharding實現水平擴充套件
  • 部署方便

使用MongoDB,你得記住以下幾點:

  • MongoDB 假設你有大磁碟空間
  • MongoDB 假設你的記憶體也足夠大於放下你的熱資料
  • MongoDB 假設你是部署在64位系統上的(32位有2G的限制,試用還可以)
  • MongoDB 假設你的系統是little-endian的
  • MongoDB 假設你有多臺機器(並不專注於單機可靠性)
  • MongoDB 假設你希望用安全換效能,同時允許你用效能換安全

MongoDB在下面領域不太擅長

  • 不太穩定,特別是auto-sharding目前還有很多問題
  • 不支援SQL,這意味著你很多透過SQL介面的工具不再適用
  • 持久化,MongoDB單機可靠性不太好,當機可能丟失一段時間的資料
  • 相關文件比較少,新功能都有這個問題
  • 相關人才比較難找,這也是新功能的問題之一

MongoDB安全性初探

本文是一篇轉載文章,文章主要介紹了MongoDB 安全性方面的知識,包括 MongoDB 安全配製、認證機制及認證的網路流程,也簡單介紹了可能利用JavaScript引擎執行指令碼攻擊的可能。

MongoDB,這麼火的玩意其實早就想好好研究一下了。之前一直沒空仔細學學新的東西,總是感覺精力不足。這次趁著買了一本書,就零零散散地在VPS上搭建、測試、看實現程式碼。感覺也蠻有意思的一個資料庫。雖然感覺它非常簡單,尤其是看程式碼的時候更是感覺如此。但這不也是另一個的典範麼,還是簡單但是實用的東西最能流行。

既然都看了其實現,也不能不產出點什麼。正好多年沒更新博文,就簡單分析一下 MongoDB 的安全性,湊個數先。

預設配置的安全情況

在預設情況下,mongod是監聽在0.0.0.0之上的。而任何客戶端都可以直接連線27017,且沒有認證。好處是,開發人員或dba可以即時上手,不用擔心被一堆配置弄的心煩意亂。壞處是,顯而易見,如果你直接在公網伺服器上如此搭建MongoDB,那麼所有人都可以直接訪問並修改你的資料庫資料了。

預設情況下,mongod也是沒有管理員賬戶的。因此除非你在admin資料庫中使用db.addUser()命令新增了管理員帳號,且使用–auth引數啟動mongod,否則在資料庫中任何人都可以無需認證執行所有命令。包括delete和shutdown。

此外,mongod還會預設監聽28017埠,同樣是繫結所有ip。這是。從中可以獲取到資料庫當前連線、log、狀態、執行系統等資訊。如果你開啟了–rest引數,甚至可以直接透過web介面查詢資料,執行mongod命令。

我試著花了一個晚上掃描了國內一個B段,國外一個B段。結果是國外開了78個MongoDB,而國內有60臺。其中我隨意挑選了10臺嘗試連線,而只有一臺機器加了管理員賬戶做了認證,其他則全都是不設防的城市。可見其問題還是比較嚴重的。

其實MongoDB本身有非常詳細的安全配置準則,顯然他也是想到了,然而他是將安全的任務推給使用者去解決,這本身的策略就是偏向易用性的,對於安全性,則得靠邊站了。

使用者資訊儲存及認證過程

類似MySQL將系統使用者資訊儲存在mysql.user表。MongoDB也將系統使用者的username、pwd儲存在admin.system.users集合中。其中。這本身並沒有什麼問題。username和:mongo:相當於對原密碼加了一個salt值,即使攻擊者獲取了資料庫中儲存的md5 hash,也沒法簡單的從彩虹表中查出原始密碼。

我們再來看看MongoDB對客戶端的認證互動是如何實現的。mongo client和server互動都是基於明文的,因此很容易被網路嗅探等方式抓取。這裡我們使用資料庫自帶的,可以直接dump出客戶端和服務端的所有互動資料包:

[root@localhost bin]# ./mongosniff --source NET lo
sniffing 27017 
 
...//省略開頭的資料包,直接看看認證流程。以下就是當使用者輸入db.auth(username, real_passwd)後發生的互動。
 
127.0.0.1:34142  -->> 127.0.0.1:27017 admin.  62 bytes  id:8        8
        query: { getnonce: 1.0 }  ntoreturn: -1 ntoskip: 0
127.0.0.1:27017  <<--  127.0.0.1:34142   81 bytes  id:7 7 - 8
        reply n:1 cursorId: 0
        { nonce: "df97182fb47bd6d0", ok: 1.0 }
127.0.0.1:34142  -->> 127.0.0.1:27017 admin.  152 bytes  id:9       9
        query: { authenticate: 1.0, user: "admin", nonce: "df97182fb47bd6d0", key: "3d839522b547931057284b6e1cd3a567" }  ntoreturn: -1 ntoskip: 0
127.0.0.1:27017  <<--  127.0.0.1:34142   53 bytes  id:8 8 - 9
        reply n:1 cursorId: 0
        { ok: 1.0 }
  • 第一步,client向server傳送一個命令getnonce,向server申請一個隨機值nonce,server返回一個16位的nonce。這裡每次返回的值都不相同。
  • 第二步,client將使用者輸入的明文密碼透過演算法生成一個key,即 ,並將之連同使用者名稱、nonce一起返回給server,server收到資料,首先比對nonce是否為上次生成的nonce,然後比對 key == md5(nonce + username + pwd)。如果相同,則驗證透過

由於至始至終沒有密碼hash在網路上傳輸,而是使用了類似挑戰的機制,且每一次nonce的值都不同,因此即使攻擊者擷取到key的值,也沒用辦法透過重放攻擊透過認證。

然而當攻擊者獲取了資料庫中儲存的pwd hash,則認證機制就不會起到作用了。即使攻擊者沒有破解出pwd hash對應的密碼原文。但是仍然可以透過傳送md5(nonce + username + pwd)的方式直接透過server認證。這裡實際上server是將使用者的pwd hash當作了真正的密碼去驗證,並沒有基於原文密碼驗證。在這點上和我之前分析的mysql的認證機制其實沒什麼本質區別。當然或許這個也不算是認證機制的弱點,但是畢竟要獲取MongoDB的username和pwd的可能性會更大一些。

然而在Web的監控介面的認證中則有一些不同。當client來源不是localhost,這裡的使用者認證過程是基於HTTP 401的。其過程與mongo認證大同小異。但是一個主要區別是:這裡的nonce並沒有隨機化,而是。

利用這個特點,如果攻擊者抓取了管理員一次成功的登入,那麼他就可以重放這個資料包,直接進入Web監控頁面。

同樣,攻擊者還可以透過此介面直接暴力破解 mongo 的使用者名稱密碼。實際上27017和28017都沒有對密碼猜解做限制,但Web由於無需每次獲取nonce,因此將會更為簡便。

JavaScript的執行與保護

MongoDB 本身最大的特點之一,就是他是使用 JavaScript 語言作為命令驅動的。駭客會比較關注這一點,因為其命令的支援程度,就是獲取 MongoDB 許可權之後是否能進一步滲透的關鍵。

JavaScript 本身的標準庫其實相當弱。無論是 spidermonkey 或者是 v8 引擎,其實都沒有系統操作、檔案操作相關的支援。對此,。可以看到,ls/cat/cd/hostname 甚至 runProgram 都已經在 JavaScript 的上下文中有實現。看到這裡是不是已經迫不及待了?mongoshell 中試一下輸入ls(“./”),看看返回。

等等?結果怎麼這麼熟悉?哈哈,沒錯,其實這些 api 都是在 client 的上下文中實現的。一個小小玩笑。

那麼在server端是否可以執行js呢?答案是肯定的。利用 db.eval(code) ——實際上底層執行的是db.$cmd.findOne({$eval: code}) —— 可以在server端執行我們的js程式碼。

當然在。顯然 mongod 考慮到了安全問題(也可能是其他原因),因此在這裡並沒有提供client中這麼強大的功能。當然 MongoDB 還在不斷更新,長期關注這個list,說不定以後就有類似 load_file/exec 之類的實現。

一勞永逸解決服務端js執行帶來的問題可以使用引數。直接禁止server-side的js程式碼執行功能。

 

 

按照,MongoDB是一種可擴充套件的高效能的開源的面向文件(document-oriented )的資料庫,採用C++開發。注意mongo不是mango(芒果),這個詞是從humongous中擷取出來的,其野心不言而明,直指海量資料儲存。和其他很多NoSQL不太一樣,MongoDB背後有一個專門的商業公司在提供支援和推廣,有點類似MySQL AB的模式。這一系列文章,是為入門者寫的,已經對NoSQL和MongoDB有一定研究和經驗的,可以略過,或者看看如有疏漏,請留言指出。

面向文件,那麼什麼是文件呢?很明顯這不是我們常見的word文件。這裡說的文件,是一種可以巢狀的資料集合。從關聯式資料庫的正規化的概念來說,巢狀是明顯的反正規化設計。正規化設計的好處是消除了依賴,但是增加了關聯,查詢需要透過關聯兩張或者多張表來獲得所需要的全部資料,但是更改操作是原子的,只需要修改一個地方即可。反正規化則是增加了資料冗餘來提升查詢效能,但更新操作可能需要更新冗餘的多處資料,需要注意一致性的問題。

一個典型的例子,如blog,關聯式資料庫中一般可以把文章設計為一張表,評論設計為一張表,那麼在頁面需要展示一篇文章和其對應的評論的時候,就需要關聯查詢文章表和評論表。但是面向文件的設計,可以將評論作為文章的一個巢狀文件存放在一起,這不但省去了關聯查詢,由於儲存在一起,查詢的效能也可以做到更好。

MongoDB的面向文件採用的是,一種類似的格式,但是是二進位制序列化的。如上面提到的blog的文章和評論,可以做如下設計:

{ 'id':1, 'author':'NinGoo', 'title':'白話MongoDB(一)', 'content':'按照官方的說法,此處省略一萬字',

    comment:[ {'comment-author':'宋兵甲', 'comment-content':'有木有' } ,

              {'comment-author':'尼瑪','comment-content':'傷不起啊' }

            ]

}

1. 相關資料存放在一起,針對性的查詢可以消除join,效能比分散儲存要高且方便。
2. 整個結構清晰自解析。所有欄位名和值都儲存,所以不需要提前設計結構,key的名字和數目可以任意指定,也就是所謂的schema-free。
3. 由於欄位名在每一行每一列都需要重複存在,會帶來一些額外的儲存消耗,這在海量資料及欄位較多的時候也需要考慮。
4. 一個document的,1.7.2之前是4MB,目前是8MB,以後可能增長到32MB。如果有更大的資料,可以使用MongoDB底層的GridFS直接作為檔案儲存。
5. 如果需要查詢某個評論者的所有評論,則相對困難。當然,MongoDB支援任意key的索引,這也不是什麼大問題。

像上面的一個結構,為一個文件(document),相當於關聯式資料庫中的一行記錄,多個文件組成一個集合(collection),相當於關聯式資料庫的表。多個集合(collection),邏輯上組織在一起,就是資料庫(database),一個MongoDB例項支援多個資料庫(database)。

大部分的NoSQL產品,為追求效能,一致性等,一般只能支援簡單的基於row-key的單條或者範圍查詢,但是MongoDB可以針對任意列的key建立索引,甚至是內嵌文件裡的key,從支援的查詢的靈活性上來看,更接近傳統的關聯式資料庫,同時還能在效能上向NoSQL看齊,加上支援複製,自動分片和Map/Reduce等功能,非常的吸引眼球,正在成為一款熱門的海量儲存產品。其背後的商業支援公司,也正在不遺餘力的推廣,前不久還在專門組織了一場技術交流會。在其首頁列舉的裡,包括foursquare,sourceforge,github等知名網際網路公司和應用,當然,也包括淘寶網。

 

 

MongoDB調查總結

與關係型資料庫相比,MongoDB的優點:
①弱一致性(最終一致),更能保證使用者的訪問速度:
舉例來說,在傳統的關係型資料庫中,一個COUNT型別的操作會鎖定資料集,這樣可以保證得到“當前”情況下的精確值。這在某些情況下,例如透過ATM檢視賬戶資訊的時候很重要,但對於Wordnik來說,資料是不斷更新和增長的,這種“精確”的保證幾乎沒有任何意義,反而會產生很大的延遲。他們需要的是一個“大約”的數字以及更快的處理速度。

但某些情況下MongoDB會鎖住資料庫。如果此時正有數百個請求,則它們會堆積起來,造成許多問題。我們使用了下面的最佳化方式來避免鎖定:
每次更新前,我們會先查詢記錄。查詢操作會將物件放入記憶體,於是更新則會盡可能的迅速。在主/從部署方案中,從節點可以使用“-pretouch”引數執行,這也可以得到相同的效果。 
使用多個mongod程式。我們根據訪問模式將資料庫拆分成多個程式。 
②文件結構的儲存方式,能夠更便捷的獲取資料。
對於一個層級式的資料結構來說,如果要將這樣的資料使用扁平式的,表狀的結構來儲存資料,這無論是在查詢還是獲取資料時都十分困難。
舉例1:
就拿一個“字典項”來說,雖然並不十分複雜,但還是會關係到“定義”、“詞性”、“發音”或是“引用”等內容。大部分工程師會將這種模型使用關係型資料庫中的主鍵和外來鍵表現出來,但把它看作一個“文件”而不是“一系列有關係的表”豈不更好?使用“dictionary.definition.partOfSpeech='noun'”來查詢也比表之間一系列複雜(往往代價也很高)的連線查詢方便且快速。

舉例2:在一個關係型資料庫中,一篇部落格(包含文章內容、評論、評論的投票)會被打散在多張資料表中。在MongoDB中,能用一個文件來表示一篇部落格,評論與投票作為文件陣列,放在正文主文件中。這樣資料更易於管理,消除了傳統關係型資料庫中影響效能和水平擴充套件性的“JOIN”操作。

CODE↓
> db.blogposts.save({ title : "My First Post", author: {name :"Jane", id :1},
  comments : [{ by: "Abe", text: "First" },
             { by : "Ada", text : "Good post" }]
})

>db.blogposts.find( { "author.name" : "Jane" } )

>db.blogposts.findOne({ title : "My First Post","author.name": "Jane",
  comments : [{ by: "Abe", text: "First" },
             { by : "Ada", text : "Good post" } ]
})
> db.blogposts.find( { "comments.by" : "Ada" } )

>db.blogposts.ensureIndex( { "comments.by" : 1 } );
舉例③:
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個屬性,屬性的型別既可以是基本的資料型別(如數字、字串、日期等),也可以是陣列或者雜湊,甚至還可以是一個子文件(embeddocument)。這樣,可以實現逆規範化(denormalizing)的資料模型,提高查詢的速度。

圖1 MongoDB是一個Schema-free的文件資料庫


  圖2是一個例子,作品和評論可以設計為一個collection,評論作為子文件內嵌在art的comments屬性中,評論的回覆則作為comment子文件的子文件內嵌於replies屬性。按照這種設計模式,只需要按照作品id檢索一次,即可獲得所有相關的資訊了。在MongoDB中,不強調一定對資料進行Normalize ,很多場合都建議De-normalize,開發人員可以扔掉傳統關聯式資料庫各種正規化的限制,不需要把所有的實體都對映為一個Collection,只需定義最頂級的class。MongoDB的文件模型可以讓我們很輕鬆就能將自己的Object對映到collection中實現儲存。

圖2 MongoDB支援嵌入子文件

③內建GridFS,支援大容量的儲存。
  GridFS是一個出色的分散式檔案系統,可以支援海量的資料儲存。
  內建了GridFS了MongoDB,能夠滿足對大資料集的快速範圍查詢。
④內建Sharding。
提供基於Range的Auto Sharding機制:一個collection可按照記錄的範圍,分成若干個段,切分到不同的Shard上。
Shards可以和複製結合,配合Replica sets能夠實現Sharding+fail-over,不同的Shard之間可以負載均衡。查詢是對客戶端是透明的。客戶端執行查詢,統計,MapReduce等操作,這些會被MongoDB自動路由到後端的資料節點。這讓我們關注於自己的業務,適當的時候可以無痛的升級。MongoDB的Sharding設計能力最大可支援約20 petabytes,足以支撐一般應用。
這可以保證MongoDB執行在便宜的PC伺服器叢集上。PC叢集擴充起來非常方便並且成本很低,避免了“sharding”操作的複雜性和成本。

⑤第三方支援豐富。(這是與其他的NoSQL相比,MongoDB也具有的優勢)
現在網路上的很多NoSQL開源資料庫完全屬於社群型的,沒有官方支援,給使用者帶來了很大的風險。
而開源文件資料庫MongoDB背後有商業公司10gen為其提供供商業培訓和支援。
而且MongoDB社群非常活躍,很多開發框架都迅速提供了對MongDB的支援。不少知名大公司和網站也在生產環境中使用MongoDB,越來越多的創新型企業轉而使用MongoDB作為和Django,RoR來搭配的技術方案。
⑥效能優越:
在使用場合下,千萬級別的文件物件,近10G的資料,對有索引的ID的查詢不會比mysql慢,而對非索引欄位的查詢,則是全面勝出。mysql實際無法勝任大資料量下任意欄位的查詢,而mongodb的查詢效能實在讓我驚訝。寫入效能同樣很令人滿意,同樣寫入百萬級別的資料,mongodb比我以前試用過的couchdb要快得多,基本10分鐘以下可以解決。補上一句,觀察過程中mongodb都遠算不上是CPU殺手。


與關係型資料庫相比,MongoDB的缺點:
①mongodb不支援事務操作。
  所以事務要求嚴格的系統(如果銀行系統)肯定不能用它。(這點和優點①是對應的)
②mongodb佔用空間過大。
  關於其原因,在官方的FAQ中,提到有如下幾個方面:
1、空間的預分配:為避免形成過多的硬碟碎片,mongodb每次空間不足時都會申請生成一大塊的硬碟空間,而且申請的量從64M、128M、256M那樣的指數遞增,直到2G為單個檔案的最大體積。隨著資料量的增加,你可以在其資料目錄裡看到這些整塊生成容量不斷遞增的檔案。

2、欄位名所佔用的空間:為了保持每個記錄內的結構資訊用於查詢,mongodb需要把每個欄位的key-value都以BSON的形式儲存,如果value域相對於key域並不大,比如存放數值型的資料,則資料的overhead是最大的。一種減少空間佔用的方法是把欄位名儘量取短一些,這樣佔用空間就小了,但這就要求在易讀性與空間佔用上作為權衡了。我曾建議作者把欄位名作個index,每個欄位名用一個位元組表示,這樣就不用擔心欄位名取多長了。但作者的擔憂也不無道理,這種索引方式需要每次查詢得到結果後把索引值跟原值作一個替換,再傳送到客戶端,這個替換也是挺耗費時間的。現在的實現算是拿空間來換取時間吧。

3、刪除記錄不釋放空間:這很容易理解,為避免記錄刪除後的資料的大規模挪動,原記錄空間不刪除,只標記“已刪除”即可,以後還可以重複利用。

4、可以定期執行db.repairDatabase()來整理記錄,但這個過程會比較緩慢

③MongoDB沒有如MySQL那樣成熟的維護工具,這對於開發和IT運營都是個值得注意的地方。

################################

Wordnik的MongoDB使用經驗
http://news.cnblogs.com/n/80856/

視覺中國的NoSQL之路:從MySQL到MongoDB
http://news.cnblogs.com/n/77959/

Wordnik的MongoDB使用經驗

Wordnik是一項線上字典及百科全書服務,在大約一年前,它們逐漸開始從MySQL遷移至文件型資料庫MongoDB,後者是著名的NoSQL產品之一。最近Wordnik的技術團隊透過官方部落格分享了這12個月來使用MongoDB經驗及現狀

  據Wordnik技術團隊描述,它們起初決定使用MongoDB,是看中了它的弱一致性(最終一致)及文件結構的儲存方式。

  在傳統的關係型資料庫中,一個COUNT型別的操作會鎖定資料集,這樣可以保證得到“當前”情況下的精確值。這在某些情況下,例如透過ATM檢視賬戶資訊的時候很重要,但對於Wordnik來說,資料是不斷更新和增長的,這種“精確”的保證幾乎沒有任何意義,反而會產生很大的延遲。他們需要的是一個“大約”的數字已經更快的處理速度。

  此外,Worknik的資料結構是“層級”式的,如果要將這樣的資料使用扁平式的,表狀的結構來儲存資料,這無論是在查詢還是獲取資料時都十分困難:

就拿一個“字典項”來說,雖然並不十分複雜,但還是會關係到“定義”、“詞性”、“發音”或是“引用”等內容。大部分工程師會將這種模型使用關係型資料庫中的主鍵和外來鍵表現出來,但把它看作一個“文件”而不是“一系列有關係的表”豈不更好?使用“dictionary.definition.partOfSpeech='noun'”來查詢也比表之間一系列複雜(往往代價也很高)的連線查詢方便且快速。

  經過了一年的使用,Worknik描述了他們從MySQL全面遷移至MongoDB後的感受。

  首先是效能上的提高,這也是使用MongoDB的主要原因。MongoDB解決了Worknik在使用MySQL的時候,在儲存和資料查詢時都遇到的一些問題。下面是一些統計資料:

·     MongoDB承受了平均50萬每小時的請求(包括週末和夜間),高峰期大約是4倍的量。

·     MongoDB中有超過120億個文件。

·     每個節點大約3TB資料。

·     一般情況下文件插入速度為每條8千條,峰值為每秒5萬條。

·     單個Java客戶端在千兆頻寬下,對單個MongoDB節點的可持續的傳輸速度為每秒10MB。同一個客戶端的四個讀取器可以保持每秒40MB的讀取速度。

·     各種形式的查詢都比MySQL的實現要快許多:

o  示例的獲取速度,從400ms減少為60ms。

o  字典項獲取速度,從20ms減少為1ms。

o  文件後設資料的獲取速度,從30ms減少為0.1ms。

o  拼寫提示的獲取速度,從10ms減少為1.2ms。

  Worknik表示,在壓力較高的情況下,MongoDB的內建快取機制,讓系統對memcached層的每次呼叫節省了1-2ms,同時還剩下了許多GB的記憶體。此外,所有的資料不可能都在記憶體中,因此獲取示例的60ms還包括磁碟訪問時間。

  其次,使用MongoDB還帶來了許多靈活性,除了之前提到的文件型儲存讓查詢變得十分迅速之外,MongoDB還帶來了其他一些好處。例如以前Worknik使用叢集檔案系統儲存音訊檔案,如今這些檔案儲存在MongoDB的GridFS中。這給IT維護帶來了許多方便,例如可以使用相同的方式來維護資料和檔案內容,資料庫和檔案也是保持同步的。

  Worknik對MongoDB的可靠性也很滿意,從四月起,MongoDB只重啟了兩次,一次是從1.4.2版升級到1.4.4版,還有一次是由於資料中心斷電。

  唯一可能的抱怨是對於維護性上的。MongoDB沒有如MySQL那樣成熟的維護工具,這對於開發和IT運營都是個值得注意的地方。不過幸運的是,MongoDB提供了許多“接入點”,因此Worknit建立了一些輔助工具,並打算開源,他們表示將在十二月份的上提供更多資訊。

  在運營過程中,資料中心斷電造成了很大的問題。由於斷電發生在寫密集的情況下,因此對主從節點都造成了損害。當時主節點正忙於將資料寫回磁碟,而從節點正在透過日誌獲取資料。在電力回覆之後,他們花費了超過24小時了來修復主節點上的資料,在這段時間內,他們將從節點提升為主節點使系統得以正常工作。

  最後,Worknik還分享了一些經驗:

資料尺寸:在四月份的MongoSF會議上,我們曾。之後10gen指出了MongoDB的集合填充機制,以及Worknik某些使用場景上造成的浪費。我們將一些物件作為子文件儲存,並去除一些索引之後,則大約使用了MySQL的1.5至2倍的儲存空間。

鎖:某些情況下MongoDB會鎖住資料庫。如果此時正有數百個請求,則它們會堆積起來,造成許多問題。我們使用了下面的最佳化方式來避免鎖定:

·        每次更新前,我們會先查詢記錄。查詢操作會將物件放入記憶體,於是更新則會盡可能的迅速。在主/從部署方案中,從節點可以使用“-pretouch”引數執行,這也可以得到相同的效果。

·        使用多個mongod程式。我們根據訪問模式將資料庫拆分成多個程式。

  MongoDB是一個可擴充套件、高效能的下一代資料庫。最新版本為1.6.3,並。

視覺中國的NoSQL之路:從MySQL到MongoDB

起因

  視覺中國網站()是國內最大的創意人群的專業網站。2009年以前,同很多公司一樣,我們的CMS和社群產品都構建於PHP+Nginx+MySQL之上;MySQL使用了Master+Master的部署方案;前端使用自己的PHP框架進行開發;Memcached作為快取;Nginx進行Web服務和負載均衡;Gearman進行非同步任務處理。在傳統的基於靜態內容(如文章,資訊,帖子)的產品,這個體系執行良好。透過分級的快取,資料庫端實際負載很輕。2009年初,我們進行了新產品的開發。此時,我們遇到了如下一些問題。

  使用者資料激增:我們的MySQL某個資訊表上線1個月的資料就達到千萬。我們之前忽略的很多資料,在新形勢下需要跟蹤記錄,這也導致了資料量的激增;

  使用者對於資訊的實時性要求更高:對資訊的響應速度和更新頻度就要求更高。簡單透過快取解決的靈丹妙藥不復存在;

  對於Scale-out的要求更高:有些創新產品的增長速度是驚人的。因此要求能夠無痛的升級擴充套件,否則一旦停機,那麼使用者流失的速度也是驚人的;

  大量檔案的備份工作:我們面向的是創意人群,產生的內容是以圖片為主。需要能夠對這些圖片及不同尺寸的縮圖進行有效的備份管理。我們之前使用的Linux inotify+rsync的增量備份方案效果不佳;

  需求變化頻繁:開發要更加敏捷,開發成本和維護成本要更低,要能夠快速地更新進化,新功能要在最短的週期內上線。

  最初,我們試圖完全透過最佳化現有的技術架構來解決以上問題:對資料時效性進一步分級分層快取,減小快取粒度;改進快取更新機制(線上實時和線下非同步更新)提高快取命中率;嘗試對業務資料的特點按照水平和垂直進行分表;使用MogileFS進行分佈儲存;進一步最佳化MySQL的效能,同時增加MySQL節點等。但很快發現,即便實施了上述方案,也很難完全解決存在的問題:過度依賴Memcached導致資料表面一致性的維護過於複雜,應用程式開發需要很小心,很多時候出現Memcached的失效會瞬間導致後端資料庫壓力過大;不同型別資料的特點不同,資料量差別也很大;分表的機制和方式在效率平衡上很難取捨;MogileFS對我們而言是腳小鞋大,維護成本遠遠超過了實際的效益;引入更多的MySQL資料庫節點增大了我們的維護量,如何有效監控和管理這些節點又成了新的問題。雖然虛擬化可以解決部分問題,但還是不能令人滿意;

  除了MySQL,能否找到一個更為簡單、輕便的瑞士軍刀呢?我們的目光投向了NoSQL的方案。

  候選方案

  最初,對於NoSQL的候選方案,我依據關注和熟悉程度,並且在甄別和選擇合適的方案時特別制定了一些原則:是否節省系統資源,對於CPU等資源是否消耗過大;客戶端/API支援,這直接影響應用開發的效率;文件是否齊全,社群是否活躍;部署是否簡單;未來擴充套件能力。按以上幾點經過一段測試後,我們候選名單中剩下Redis、MongoDB和Flare。

  Redis對豐富資料型別的操作很吸引人,可以輕鬆解決一些應用場景,其讀寫效能也相當高,唯一缺點就是儲存能力和記憶體掛鉤,這樣如果儲存大量的資料需要消耗太多的記憶體(最新的版本已經不存在這個問題)。

  Flare的叢集管理能力令人印象深刻,它可以支援節點的動態部署,支援節點的基於權重的負載均衡,支援資料分割槽。同時允許儲存大的資料,其key的長度也不受Memcached的限制。而這些對於客戶端是透明的,客戶端使用Memcached協議連結到Flare的proxy節點就可以了。由於使用叢集,Flare支援fail-over,當某個資料節點宕掉,對於這個節點的訪問都會自動被proxy節點forward到對應的後備節點,恢復後還可以自動同步。Flare的缺點是實際應用案例較少,文件較為簡單,目前只在Geek使用。

  以上方案都打算作為一個最佳化方案,我從未想過完全放棄MySQL。然而,用MongoDB做產品的設計原型後,我徹底被征服了,決定全面從MySQL遷移到MongoDB。

  為什麼MongoDB可以替代MySQL?

  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支援範圍查詢,正規表示式查詢,對子文件內屬性的查詢,可以取代原來大多數任務的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一樣,歷史總是驚人的相似,讓我們拭目以待吧!

  作者簡介:

  潘凡(nightsailer,N.S.), 視覺中國網站技術總監,聯合創始人,家有1狗2貓。目前負責網站平臺設計和底層產品研發工作。當前關注:Apps平臺設計、分散式檔案儲存、NoSQL、高效能後現代的Perl程式設計。Twitter:@nightsailer  Blog:

Wordnik的MongoDB使用經驗

Wordnik是一項線上字典及百科全書服務,在大約一年前,它們逐漸開始從MySQL遷移至文件型資料庫MongoDB,後者是著名的NoSQL產品之一。最近Wordnik的技術團隊透過官方部落格分享了這12個月來使用MongoDB經驗及現狀

  據Wordnik技術團隊描述,它們起初決定使用MongoDB,是看中了它的弱一致性(最終一致)及文件結構的儲存方式。

  在傳統的關係型資料庫中,一個COUNT型別的操作會鎖定資料集,這樣可以保證得到“當前”情況下的精確值。這在某些情況下,例如透過ATM檢視賬戶資訊的時候很重要,但對於Wordnik來說,資料是不斷更新和增長的,這種“精確”的保證幾乎沒有任何意義,反而會產生很大的延遲。他們需要的是一個“大約”的數字已經更快的處理速度。

  此外,Worknik的資料結構是“層級”式的,如果要將這樣的資料使用扁平式的,表狀的結構來儲存資料,這無論是在查詢還是獲取資料時都十分困難:

就拿一個“字典項”來說,雖然並不十分複雜,但還是會關係到“定義”、“詞性”、“發音”或是“引用”等內容。大部分工程師會將這種模型使用關係型資料庫中的主鍵和外來鍵表現出來,但把它看作一個“文件”而不是“一系列有關係的表”豈不更好?使用“dictionary.definition.partOfSpeech='noun'”來查詢也比表之間一系列複雜(往往代價也很高)的連線查詢方便且快速。

  經過了一年的使用,Worknik描述了他們從MySQL全面遷移至MongoDB後的感受。

  首先是效能上的提高,這也是使用MongoDB的主要原因。MongoDB解決了Worknik在使用MySQL的時候,在儲存和資料查詢時都遇到的一些問題。下面是一些統計資料:

·     MongoDB承受了平均50萬每小時的請求(包括週末和夜間),高峰期大約是4倍的量。

·     MongoDB中有超過120億個文件。

·     每個節點大約3TB資料。

·     一般情況下文件插入速度為每條8千條,峰值為每秒5萬條。

·     單個Java客戶端在千兆頻寬下,對單個MongoDB節點的可持續的傳輸速度為每秒10MB。同一個客戶端的四個讀取器可以保持每秒40MB的讀取速度。

·     各種形式的查詢都比MySQL的實現要快許多:

o  示例的獲取速度,從400ms減少為60ms。

o  字典項獲取速度,從20ms減少為1ms。

o  文件後設資料的獲取速度,從30ms減少為0.1ms。

o  拼寫提示的獲取速度,從10ms減少為1.2ms。

  Worknik表示,在壓力較高的情況下,MongoDB的內建快取機制,讓系統對memcached層的每次呼叫節省了1-2ms,同時還剩下了許多GB的記憶體。此外,所有的資料不可能都在記憶體中,因此獲取示例的60ms還包括磁碟訪問時間。

  其次,使用MongoDB還帶來了許多靈活性,除了之前提到的文件型儲存讓查詢變得十分迅速之外,MongoDB還帶來了其他一些好處。例如以前Worknik使用叢集檔案系統儲存音訊檔案,如今這些檔案儲存在MongoDB的GridFS中。這給IT維護帶來了許多方便,例如可以使用相同的方式來維護資料和檔案內容,資料庫和檔案也是保持同步的。

  Worknik對MongoDB的可靠性也很滿意,從四月起,MongoDB只重啟了兩次,一次是從1.4.2版升級到1.4.4版,還有一次是由於資料中心斷電。

  唯一可能的抱怨是對於維護性上的。MongoDB沒有如MySQL那樣成熟的維護工具,這對於開發和IT運營都是個值得注意的地方。不過幸運的是,MongoDB提供了許多“接入點”,因此Worknit建立了一些輔助工具,並打算開源,他們表示將在十二月份的上提供更多資訊。

  在運營過程中,資料中心斷電造成了很大的問題。由於斷電發生在寫密集的情況下,因此對主從節點都造成了損害。當時主節點正忙於將資料寫回磁碟,而從節點正在透過日誌獲取資料。在電力回覆之後,他們花費了超過24小時了來修復主節點上的資料,在這段時間內,他們將從節點提升為主節點使系統得以正常工作。

  最後,Worknik還分享了一些經驗:

資料尺寸:在四月份的MongoSF會議上,我們曾。之後10gen指出了MongoDB的集合填充機制,以及Worknik某些使用場景上造成的浪費。我們將一些物件作為子文件儲存,並去除一些索引之後,則大約使用了MySQL的1.5至2倍的儲存空間。

鎖:某些情況下MongoDB會鎖住資料庫。如果此時正有數百個請求,則它們會堆積起來,造成許多問題。我們使用了下面的最佳化方式來避免鎖定:

·        每次更新前,我們會先查詢記錄。查詢操作會將物件放入記憶體,於是更新則會盡可能的迅速。在主/從部署方案中,從節點可以使用“-pretouch”引數執行,這也可以得到相同的效果。

·        使用多個mongod程式。我們根據訪問模式將資料庫拆分成多個程式。

  MongoDB是一個可擴充套件、高效能的下一代資料庫。最新版本為1.6.3,並。

 

關於MongoDB,你可能不知道的十件事

MongoDB 很簡單,參照著一些常用的教程下載相應平臺的二進位制包、建立dbpath然後啟動基本上就可以跑了。但是如果你真的打算在生產環境中使用MongoDB,還是請多進行深入的研究,下面是一位MongoDB的愛好者在參加完MongoNYC大會後總結的十個自己瞭解到的知識點,看看有沒有你不知道的吧。

·     1.MongoDB有一個大的全域性鎖,這使得一個MongoDBDaemon只能同時進行一個寫操作,即使是對不同collection的操作,也只得排隊。

·     2.MongoDB並沒有一個基於統計的查詢最佳化器,對查詢併發的執行多個不同的計劃,在最快的那個返回後就終止其它任務,並將這個最快的計劃指導查詢。當然不是每次查詢都執行多個不同計劃,這個會隔一段時間執行一次。

·     3.Mongos只有在你使用Sharding時才需要,在不用Sharding時,實際上是客戶端來實現負載均衡的。

·     4.MongoDB不僅僅只有Replica Sets,還有傳統的Master-Slave模式。(實際上你想配置成Master-Master也完全可以)

·     5.MongoDB的同步機器支援“slave-delay”引數,這個引數指定Slave機器延遲Master多長時間。這個引數用來做準備非常合適。

·     6.MongoDB 使用了mmap,在32位系統下資料檔案只能達到2G,所以32位系統下的MongoDB玩玩就夠了。

·     7.MongoDB會在日誌裡記錄執行時間超過100ms的操作,實際上這個是可以靈活配置的。

·     8.MongoDB可以執行一些耗時較長的統計分析任務。

·     9.MongoDB不支援多主對單從的架構(這個應該是支援的,原文作者可能理解錯了)。

·     10.MongoDB的Replica Sets 模式下,可以設定一些節點為Arbiter,它們不儲存資料,只在需要重新選Primary時參與投票。

Riak與MongoDB的對比

機制與概念上的異同

Riak和MongoDB在使用特性上有下面幾個相同點:

  • 都是文件型的資料模型
  • 具體儲存方式都不是以文件型進行儲存
  • 寫效能及寫吞吐都很高

雖然上面幾點看起來二者挺像,但在內部實現上兩者卻是相去甚遠。比如Riak是一個分散式的儲存,而MongoDB可以理解為是一個單一的資料庫系統,同時加上了Replication和Sharding功能。MongoDB的內部資料結構上還是文件,而Riak是不用關心儲存內容的二進位制。MongoDB提供GridFS機制來儲存二進位制內容,而Riak的二進位制內容與普通內容儲存方式一樣。MongoDB的寫入方式是 in-place方式,修改一個文件是原子性的,而Riak是透過quormNRW的機制保證寫入操作安全性的。

複製備份及橫向擴充套件

Riak主要透過一致性hash演算法來實現其資料的複製及分片,一致性hash機制是Riak的核心思想之一。在Riak中,每個節點都是對等的,所以其不存在單點故障。

而MongoDB在1.6版本後也推出了強有力的複製備份功能

1.主從複製

2.Replica Sets

Replica Sets是MongoDB的重頭功能之一,它讓幾個節點組成一個集合,在這個集合中的節點中有一個主機提供寫入,其它節點會從主機上備份資料,主機故障後會自動在從機中選取產生新的主機。

而在資料分片上,MongoDB提供了一種叫auto-sharding的機制,使資料在多個節點間可以均勻分佈,提供動態新增刪除節點的功能。

資料分片的自動調整

Riak基於一致性hash策略,在有節點從hash環上移除後,其資料會自動分攤整個環上的其它節點上。其負載也就被均勻分攤了。而MongoDB也支援在Sharding中摘除節點後的自動資料遷移,具體見此文:

效能對比

Riak的儲存引擎本身是作為外掛的形式掛載的,Riak支援BitCask,InnoDB和LevelDB等儲存引擎,使用預設的BitCask引擎,你可以在效能和資料持久化的選擇上進行調節。相比之下,MongoDB由於採用了mmap機制,如果索引和熱資料能被記憶體完全裝下,那麼其操作基本上相當於記憶體操作,所以MongoDB的當機效能是相當高的。

資料模型

Riak的資料儲存沒有特定的格式需求,它允許你儲存不同體積的文件型資料,另外Riak還可以在資料間建立link來為資料建立關聯。

MongoDB的資料是以BSON格式儲存的,你可以在MongoDB中儲存任意JSON格式的文件,在儲存時會被轉成BSON進行儲存,另外二進位制資料也可以轉換成相應的一種BSON資料型別進行儲存,GridFS正是基於這種型別來實現的。

查詢語句及分散式操作

Riak只提供key-value式的資料操作介面,它支援key-value資料的各種操作,也支援link-walking和MapReduce操作,像二級索引這種東西,在Riak裡是不存在的,因為Riak根本不關心它存的資料是什麼樣的,value對它來說只是一串資料。

MongoDB提供與關係型資料庫類似的各種資料操作(除了關聯查詢),其索引機制更是與關係型資料庫幾乎一模一樣。同時MongoDB也提供MapReduce的操作介面,用以處理一些批次任務。

衝突解決策略

Riak使用vector-clock機制來進行衝突檢測,所以其衝突解決的選擇權是留給應用層來做的。應用層可以決定兩個使用者對同一行資料的更新哪一個會勝出。

MongoDB使用的是最近更新者勝出的方式,相對來說更簡單直接。

API

Riak提供給非Erlang的客戶端兩種操作方式

  • 1. 
  • 2. 

MongoDB的協議是自己制定的一套特有協議,其客戶端由其所屬的公司開發並維護,基本主流的語言都有相應的官方客戶端。

BSON特性探討及基於其特性的MongoDB最佳化

是由10gen開發的一個資料格式,目前主要用於MongoDB中,是MongoDB的資料儲存格式。BSON基於JSON格式,選擇JSON進行改造的原因主要是JSON的通用性及JSON的schemaless的特性。

BSON主要會實現以下三點目標:

1.更快的遍歷速度

對JSON格式來說,太大的JSON結構會導致資料遍歷非常慢。在JSON中,要跳過一個文件進行資料讀取,需要對此文件進行掃描才行,需要進行麻煩的資料結構匹配,比如括號的匹配,而BSON對JSON的一大改進就是,它會將JSON的每一個元素的長度存在元素的頭部,這樣你只需要讀取到元素長度就能直接seek到指定的點上進行讀取了。

MongoDB最佳化:對於MongoDB來說,由於採用了MMAP來做記憶體與資料檔案的對映,在更新或者獲取Document的某一個欄位時,如果需要先讀取其前面的所有欄位,會導致實體記憶體由於讀操作被載入到不必要的欄位上,導致資源的不合理分配。而採用BSON只需要讀到相應的位置然後跨過無用內容讀取需要內容即可。

2.操作更簡易

對JSON來說,資料儲存是無型別的,比如你要修改基本一個值,從9到10,由於從一個字元變成了兩個,所以可能其後面的所有內容都需要往後移一位才可以。而使用BSON,你可以指定這個列為數字列,那麼無論數字從9長到10還是100,我們都只是在儲存數字的那一位上進行修改,不會導致資料總長變大。當然,在MongoDB中,如果數字從整形增大到長整型,還是會導致資料總長變大的。

MongoDB最佳化:所以使用MongoDB的一個技巧是將長度可能變化的欄位儘量命名靠後(MongoDB在update操作後會將欄位按key值按字母順序重排,所以靠後的意思是按a-z的順序取名)。這樣在更新的時候如果導致數字變長,不需要移動大量資料。一個典型的例子是如果用二進位制型別儲存檔案時,如果檔名或者檔案描述可能會變長,那麼儘量將這個欄位取名靠後是一個明智的選擇,否則在檔名或檔案描述欄位變化時,會導致移動很長的二進位制資料,造成不必要的浪費。

3.增加了額外的資料型別

JSON是一個很方便的資料交換格式,但是其型別比較有限。BSON在其基礎上增加了“byte array”資料型別。這使得二進位制的儲存不再需要先base64轉換後再存成JSON。大大減少了計算開銷和資料大小。

當然,在有的時候,BSON相對JSON來說也並沒有空間上的優勢,比如對{“field”:7},在JSON的儲存上7只使用了一個位元組,而如果用BSON,那就是至少4個位元組(32位)

MongoDB最佳化:在MongoDB中,如果你的欄位是數字型,並且涉及到資料加減操作的,那麼建議存在int型,但如果是一個固定不變的數字,並且在四位以下的話,可以考慮存成字串型別。這樣會節省空間。

目前在10gen的努力下,BSON已經有了針對多種語言的編碼解碼包。並且都是Apache 2 license下開源的。並且還在隨著MongoDB進一步地發展。關於BSON,你可以在其官方網站  上獲取更多資訊。

MySQL和MongoDB設計例項對比

本文轉載自,文章舉了一個資料庫設計的例子,對MySQLMongoDB兩種儲存工具,分別進行了資料庫結構設計,在MongoDB的設計上,利用了MongoDB的 schema-free的特性。

雖然文中的例子不一定是最優的選擇。但分享此文,希望提醒大家,換個儲存,不僅是換一個儲存,更重要的是換一套思維。

MySQL是關係型資料庫中的明星,MongoDB是文件型資料庫中的翹楚。下面透過一個設計例項對比一下二者:假設我們正在維護一個手機產品庫,裡面除了包含手機的名稱,品牌等基本資訊,還包含了待機時間,外觀設計等引數資訊,應該如何存取資料呢?

如果使用MySQL的話,應該如何存取資料呢?

如果使用MySQL話,手機的基本資訊單獨是一個表,另外由於不同手機的引數資訊差異很大,所以還需要一個參數列來單獨儲存。

CREATE TABLE IF NOT EXISTS `mobiles` (
    `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
    `name` VARCHAR(100) NOT NULL,
    `brand` VARCHAR(100) NOT NULL,
    PRIMARY KEY (`id`)
);
 
CREATE TABLE IF NOT EXISTS `mobile_params` (
    `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
    `mobile_id` int(10) unsigned NOT NULL,
    `name` varchar(100) NOT NULL,
    `value` varchar(100) NOT NULL,
    PRIMARY KEY (`id`)
);
 
INSERT INTO `mobiles` (`id`, `name`, `brand`) VALUES
(1, 'ME525', '摩托羅拉'),
(2, 'E7'   , '諾基亞');
 
INSERT INTO `mobile_params` (`id`, `mobile_id`, `name`, `value`) VALUES
(1, 1, '待機時間', '200'),
(2, 1, '外觀設計', '直板'),
(3, 2, '待機時間', '500'),
(4, 2, '外觀設計', '滑蓋');

注:為了演示方便,沒有嚴格遵守關係型資料庫的正規化設計。

如果想查詢待機時間大於100小時,並且外觀設計是直板的手機,需按照如下方式查詢:

SELECT * FROM `mobile_params` WHERE name = '待機時間' AND value > 100;
SELECT * FROM `mobile_params` WHERE name = '外觀設計' AND value = '直板';

注:參數列為了方便,把數值和字串統一儲存成字串,實際使用時,MySQL允許在字串型別的欄位上進行數值型別的查詢,只是需要進行型別轉換,多少會影響一點效能。

兩條SQL的結果取交集得到想要的MOBILE_IDS,再到mobiles表查詢即可:

SELECT * FROM `mobiles` WHERE mobile_id IN (MOBILE_IDS)

如果使用MongoDB的話,應該如何存取資料呢?

如果使用MongoDB的話,雖然理論上可以採用和MySQL一樣的設計方案,但那樣的話就顯得無趣了,沒有發揮出MongoDB作為文件型資料庫的優點,實際上使用MongoDB的話,和MySQL相比,形象一點來說,可以合二為一:

db.getCollection("mobiles").ensureIndex({
    "params.name": 1,
    "params.value": 1
});
 
db.getCollection("mobiles").insert({
    "_id": 1,
    "name": "ME525",
    "brand": "摩托羅拉",
    "params": [
        {"name": "待機時間", "value": 200},
        {"name": "外觀設計", "value": "直板"}
    ]
});
 
db.getCollection("mobiles").insert({
    "_id": 2,
    "name": "E7",
    "brand": "諾基亞",
    "params": [
        {"name": "待機時間", "value": 500},
        {"name": "外觀設計", "value": "滑蓋"}
    ]
});

如果想查詢待機時間大於100小時,並且外觀設計是直板的手機,需按照如下方式查詢:

db.getCollection("mobiles").find({
    "params": {
        $all: [
            {$elemMatch: {"name": "待機時間", "value": {$gt: 100}}},
            {$elemMatch: {"name": "外觀設計", "value": "直板"}}
        ]
    }
});

注:查詢中用到的,等高階用法的詳細介紹請參考官方文件中相關說明。

MySQL需要多個表,多次查詢才能搞定的問題,MongoDB只需要一個表,一次查詢就能搞定,對比完成,相對MySQL而言,MongoDB顯得更勝一籌,至少本例如此。

 

 

 

關於NoSQL,你必須知道的九件事

1.  理解ACID與BASE的區別(ACID是關係型資料庫強一致性的四個要求,而BASE是NoSQL資料庫通常對可用性及一致性的弱要求原則,它們的意思分別是,ACID:atomicity, consistency, isolation, durability;BASE:BasicallyAvailable, Soft-state, Eventually Consistent。同時有意思的是ACID在英語裡意為酸,BASE意思為鹼)

2.  理解持久化與非持久化的區別。這麼說是因為有的NoSQL系統是純記憶體儲存的。

3.  你必須意識到傳統有關係型資料庫與NoSQL系統在資料結構上的本質區別。傳統關係型資料庫通常是基於行的表格型儲存,而NoSQL系統包括了列式儲存(Cassandra)、key/value儲存(Memcached)、文件型儲存(CouchDB)以及圖結構儲存(Neo4j)

4.  與傳統關聯式資料庫有統一的SQL語言操作介面不同,NoSQL系統通常有自己特有的API介面。

5.  在架構上,你必須搞清楚,NoSQL系統是被設計用於成百上千臺機器的叢集中的,而非共享型資料庫系統的架構。

6.  在NoSQL系統中,可能你得習慣一下不知道你的資料具體存在何處的情況。

7.  在NoSQL系統中,你最好習慣它的弱一致性。”eventually consistent”(最終一致性)正是BASE原則中的重要一項。比如在Twitter,你在Followers列表中經常會感受到資料的延遲。

8.  在NoSQL系統中,你要理解,很多時候資料並不總是可用的。

9.  你得理解,有的方案是擁有分割槽容忍性的,有的方案不一定有。

antirez 的Redis 宣言!

1.  Redis 是一個運算元據結構的語言工具,它提供基於TCP的協議以操作豐富的資料結構。在Redis中,資料結構這個詞的意義不僅表示在某種資料結構上的操作,更包括了結構本身及這些操作的時間空間複製度。

2.  Redis 定位於一個記憶體資料庫,正是由於記憶體的快速訪問特性,才使得Redis能夠有如此高的效能,才使得Redis能夠輕鬆處理大量複雜的資料結構,Redis會嘗試其它的儲存方面的選擇,但是永遠不會改變它是一個記憶體資料庫的角色。

3.  Redis 使用基礎的API操作基礎的資料結構,Redis的API與資料結構一樣,都是一些最基礎的元素,你幾乎可以將任何資訊互動使用此API格式表示。作者調侃說,如果有其它非人類的智慧生物存在,他們也能理解Redis的API。因為它是如此的基礎。

4.  Redis 有著詩一般優美的程式碼,經常有一些不太瞭解Redis 原則的人會建議Redis採用一些其它人的程式碼,以實現一些Redis 未實現的功能,但這對我們來說就像是非要給《紅樓夢》接上後四十回一樣。(作者此處用了莎士比亞的比喻)

5.  Redis 始終避免複雜化,我們認為設計一個系統的本質,就是與複雜化作戰。我們不會為了一個小功能而往原始碼裡新增上千行程式碼,解決複雜問題的方法就是讓複雜問題永遠不要提複雜的問題。

6.  Redis 支援兩個層成的API,第一個層面包含部分操作API,但它支援用於分散式環境下的Redis。第二個層面的API支援更復雜的multi-key操作。它們各有所長,但是我們不會推出兩者都支援的API,但我們希望能夠提供例項間資料遷移的命令,並執行multi-key操作。

7.  我們以最佳化程式碼為樂,我們相信編碼是一件辛苦的工作,唯一對得起這辛苦的就是去享受它。如果我們在編碼中失去了樂趣,那最好的解決辦法就是停下來。我們決不會選擇讓Redis不好玩的開發模式。

NoSQL開篇——為什麼要使用NoSQL

NoSQL在2010年風生水起,大大小小的Web站點在追求高效能高可靠性方面,不由自主都選擇了NoSQL技術作為優先考慮的方面。今年伊始,InfoQ中文站有幸邀請到鳳凰網的孫立先生,為大家分享他之於NoSQL方面的經驗和體會。

  非常榮幸能受邀在InfoQ開闢這樣一個關於NoSQL的專欄,InfoQ是我非常尊重的一家技術媒體,同時我也希望藉助InfoQ,在國內推動NoSQL的發展,希望跟我一樣有興趣的朋友加入進來。這次的NoSQL專欄系列將先整體介紹NoSQL,然後介紹如何把NoSQL運用到自己的專案中合適的場景中,還會適當地分析一些成功案例,希望有成功使用NoSQL經驗的朋友給我提供一些線索和資訊。

  NoSQL概念

  隨著web2.0的快速發展,非關係型、分散式資料儲存得到了快速的發展,它們不保證關係資料的ACID特性。概念在2009年被提了出來。NoSQL最常見的解釋是“non-relational”,“Not Only SQL”也被很多人接受。(“NoSQL”一詞最早於1998年被用於一個輕量級的關聯式資料庫的名字。)

  NoSQL被我們用得最多的當數key-value儲存,當然還有其他的文件型的、列儲存、圖型資料庫、xml資料庫等。在NoSQL概念提出之前,這些資料庫就被用於各種系統當中,但是卻很少用於web網際網路應用。比如cdb、qdbm、bdb資料庫。

  傳統關聯式資料庫的瓶頸

  傳統的關聯式資料庫具有不錯的效能,高穩定型,久經歷史考驗,而且使用簡單,功能強大,同時也積累了大量的成功案例。在網際網路領域,MySQL成為了絕對靠前的王者,毫不誇張的說,MySQL為網際網路的發展做出了卓越的貢獻。

  在90年代,一個網站的訪問量一般都不大,用單個資料庫完全可以輕鬆應付。在那個時候,更多的都是靜態網頁,動態互動型別的網站不多。

  到了最近10年,網站開始快速發展。火爆的論壇、部落格、sns、微博逐漸引領web領域的潮流。在初期,論壇的流量其實也不大,如果你接觸網路比較早,你可能還記得那個時候還有文字型儲存的論壇程式,可以想象一般的論壇的流量有多大。

  Memcached+MySQL

  後來,隨著訪問量的上升,幾乎大部分使用MySQL架構的網站在資料庫上都開始出現了效能問題,web程式不再僅僅專注在功能上,同時也在追求效能。程式設計師們開始大量的使用快取技術來緩解資料庫的壓力,最佳化資料庫的結構和索引。開始比較流行的是透過檔案快取來緩解資料庫壓力,但是當訪問量繼續增大的時候,多臺web機器透過檔案快取不能共享,大量的小檔案快取也帶了了比較高的IO壓力。在這個時候,Memcached就自然的成為一個非常時尚的技術產品。

  Memcached作為一個獨立的分散式的快取伺服器,為多個web伺服器提供了一個共享的高效能快取服務,在Memcached伺服器上,又發展了根據hash演算法來進行多臺Memcached快取服務的擴充套件,然後又出現了一致性hash來解決增加或減少快取伺服器導致重新hash帶來的大量快取失效的弊端。當時,如果你去面試,你說你有Memcached經驗,肯定會加分的。

  Mysql主從讀寫分離

  由於資料庫的寫入壓力增加,Memcached只能緩解資料庫的讀取壓力。讀寫集中在一個資料庫上讓資料庫不堪重負,大部分網站開始使用主從複製技術來達到讀寫分離,以提高讀寫效能和讀庫的可擴充套件性。Mysql的master-slave模式成為這個時候的網站標配了。

  分表分庫

  隨著web2.0的繼續高速發展,在Memcached的快取記憶體,MySQL的主從複製,讀寫分離的基礎之上,這時MySQL主庫的寫壓力開始出現瓶頸,而資料量的持續猛增,由於MyISAM使用表鎖,在高併發下會出現嚴重的鎖問題,大量的高併發MySQL應用開始使用InnoDB引擎代替MyISAM。同時,開始流行使用分表分庫來緩解寫壓力和資料增長的擴充套件問題。這個時候,分表分庫成了一個熱門技術,是面試的熱門問題也是業界討論的熱門技術問題。也就在這個時候,MySQL推出了還不太穩定的表分割槽,這也給技術實力一般的公司帶來了希望。雖然MySQL推出了MySQL Cluster叢集,但是由於在網際網路幾乎沒有成功案例,效能也不能滿足網際網路的要求,只是在高可靠性上提供了非常大的保證。

  MySQL的擴充套件性瓶頸

  在網際網路,大部分的MySQL都應該是IO密集型的,事實上,如果你的MySQL是個CPU密集型的話,那麼很可能你的MySQL設計得有效能問題,需要最佳化了。大資料量高併發環境下的MySQL應用開發越來越複雜,也越來越具有技術挑戰性。分表分庫的規則把握都是需要經驗的。雖然有像淘寶這樣技術實力強大的公司開發了透明的中介軟體層來遮蔽開發者的複雜性,但是避免不了整個架構的複雜性。分庫分表的子庫到一定階段又面臨擴充套件問題。還有就是需求的變更,可能又需要一種新的分庫方式。

  MySQL資料庫也經常儲存一些大文字欄位,導致資料庫表非常的大,在做資料庫恢復的時候就導致非常的慢,不容易快速恢復資料庫。比如1000萬4KB大小的文字就接近40GB的大小,如果能把這些資料從MySQL省去,MySQL將變得非常的小。

  關聯式資料庫很強大,但是它並不能很好的應付所有的應用場景。MySQL的擴充套件性差(需要複雜的技術來實現),大資料下IO壓力大,表結構更改困難,正是當前使用MySQL的開發人員面臨的問題。

  NOSQL的優勢

  易擴充套件

  NoSQL資料庫種類繁多,但是一個共同的特點都是去掉關聯式資料庫的關係型特性。資料之間無關係,這樣就非常容易擴充套件。也無形之間,在架構的層面上帶來了可擴充套件的能力。

  大資料量,高效能

  NoSQL資料庫都具有非常高的讀寫效能,尤其在大資料量下,同樣表現優秀。這得益於它的無關係性,資料庫的結構簡單。一般MySQL使用Query Cache,每次表的更新Cache就失效,是一種大粒度的Cache,在針對web2.0的互動頻繁的應用,Cache效能不高。而NoSQL的Cache是記錄級的,是一種細粒度的Cache,所以NoSQL在這個層面上來說就要效能高很多了。

  靈活的資料模型

  NoSQL無需事先為要儲存的資料建立欄位,隨時可以儲存自定義的資料格式。而在關聯式資料庫裡,增刪欄位是一件非常麻煩的事情。如果是非常大資料量的表,增加欄位簡直就是一個噩夢。這點在大資料量的web2.0時代尤其明顯。

  高可用

  NoSQL在不太影響效能的情況,就可以方便的實現高可用的架構。比如Cassandra,HBase模型,透過複製模型也能實現高可用。

  總結

  NoSQL資料庫的出現,彌補了關係資料(比如MySQL)在某些方面的不足,在某些方面能極大的節省開發成本和維護成本。

  MySQL和NoSQL都有各自的特點和使用的應用場景,兩者的緊密結合將會給web2.0的資料庫發展帶來新的思路。讓關聯式資料庫關注在關係上,NoSQL關注在儲存上。

你需要知道的NoSQL資料庫10件事

NoSQL的5個優勢

  1.彈性擴充套件

  多年來,資料庫管理員一直依賴於向上擴充套件(scale up)-隨著資料庫負載的增加購買更大的資料庫伺服器―而不是向外擴充套件-隨著負載的增加將資料庫分不到多個不同的主機上。然而,隨著每秒事務數與可用性需求的提高,以及資料庫往雲或虛擬環境的遷移,向外擴充套件到廉價硬體的經濟優勢越來越難以抵擋。

  RDBMS或許比較難以在廉價的叢集上進行向外擴充套件,但是,NoSQL資料庫的新品從設計之初就是為了利用新節點的優勢進行透明擴充套件,他們通常在設計時就考慮使用低成本的廉價硬體。

  2.大資料量

  在過去10年,與每秒事務數的增長超出了認知一樣,儲存的資料的規模也出現了極大的增長。O’Reilly明智的稱此為”資料的工業革命。”RDBMS的容量也在增長以匹配這些資料的增長,但是,與每秒事務數一樣,單個RDBMS可有效管理的資料規模限制讓部分企業越來越難以忍受。今天,大規模資料量可以交由NoSQL系統來處理,比如Hadoop,超過目前最大的RDBMS可以管理的資料規模。

  3.  再見了,DBA(回頭見,DBA?)

  這些年,雖然RDBMS的提供商宣稱推出了很多的可管理性方面的改進,高階的RDBMS系統還是隻能交由昂貴的、高度受訓的DBA來進行維護。高階RDBMS系統從設計到安裝以及後續的調優,都需要DBA們深度介入。

  從理論上,通常,NoSQL資料庫的最初的設計目標就是更少的管理介入:自動修復、資料分佈以及更簡單的資料模型,從而更少的管理與調優需求。實際上,關於DBA將死的謠言很可能被略微放大了。對於任何關鍵的資料儲存,總是需要有人來關心它的效能以及可用性。

  4.經濟性

  NoSQL資料庫通常使用廉價伺服器叢集來管理暴增的資料與事務規模,而RDBMS傾向於依賴昂貴的專有伺服器與儲存系統。其結果是,NoSQL資料庫的每GB資料或每秒事務數的成本要遠遠低於RDBMS,使得你可以以更低的價格來儲存與處理更多的資料。

  5.靈活的資料模型

  在大量的生產環境資料庫中,變更管理是一個非常棘手的問題。哪怕是對資料模型的很小的變更,在RDBMS中也需要進行小心的管理,甚至還需要停機或降低服務級別。

  在資料模型的限制這一點上,NoSQL資料庫要寬鬆的多,或者完全不存在。 NoSQL的鍵值儲存(Keyvalue Store)與文件資料庫(Document Database)允許應用在一個資料單元中存入它想要的任何結構。即使是定義更加嚴格的基於BigTable的NoSQL資料庫,通常也允許建立新的欄位而不致帶來麻煩。

  其結果是,應用的變更與資料庫結構的變更不需要繫結在一個變更單元中進行管理。理論上,這可以提高應用的迭代速度,然而,顯然,如果應用無法管理資料的完整性,它將帶來不良的副作用。

  NoSQL的5個挑戰

  NoSQL資料庫的可能性空間引發了大量的關注,但是,在它們成為企業級應用的主流之前,還有大量的障礙有待克服。下面是幾個主要的挑戰。

  1.成熟度

  RDBMS已經存在了很長一段時間。 NoSQL的支持者認為它們的年紀是它們過時的象徵,但是,對於大部分CIO(資訊長)來講,RDBMS的成熟度是可以讓人放心的。通常,RDBMS 系統都很穩定,功能也很豐富。相比而言,大部分NoSQL的替代品都還處於前一生產環境階段,還有大量的關鍵特性有待實現。

  生活在科技前沿對於大部分開發人員來講,是令人興奮的,但是,企業在實施時必須非常謹慎。

  2.支援力度

  企業還希望獲得保證,當關鍵系統出現故障時,他們可以獲得及時而有效的支援。所有的RDBMS提供商都在竭盡全力地為企業提供高階別的支援。

  相比而言,大部分的NoSQL系統都是開源專案,雖然,每一個NoSQL資料庫通常都會有一家或多家公司為其提供支援,這些公司通常都是小的創業公司,沒有能力提供全球的支援,沒有足夠的支援資源,或者沒有類似於Oracle、Microsoft或者IBM的信用。

  3.分析與商業智慧

  NoSQL資料庫經過不斷的演化,已經可以滿足現代的Web 2。0應用的擴充套件需求。相應地,它們的大部分功能集也旨在滿足這些應用的需求。然而,應用程式中的資料的價值,要超出一個典型的Web應用的插入-閱讀-更新-刪除的週期。從公司資料庫中挖掘資訊以提高公司的效率與競爭力的業務,以及商業智慧(BI)是所有大中型公司的關鍵議題。

  NoSQL資料庫提供了新型的工具來做即時的查詢與分析。哪怕是一個簡單的查詢,也需要可觀的程式設計技能,通常使用的BI工具都無法訪問NoSQL資料庫。

  稍顯寬慰的是,還有類似於HIVE與PIG的這類解決方案,透過它們可以較為簡單地訪問Hadoop叢集中的資料,或許最終,可以較為簡單的訪問其他的 NoSQL資料庫。Quest軟體公司開發一個產品,Toad For Cloud Database,它提供了對各種不同的NoSQL資料庫的即時查詢功能。

  4.管理

  NoSQL的設計目標可能是提供零-管理的解決方案,但是,當前的現實是,此目標遠遠沒有實現。目前的NoSQL系統需要大量的技能來進行安裝,以及需要大量的努力來進行維護。

  5.專業技能

  坦率的講,目前世界上有上百萬的程式設計師非常熟悉RDBMS的原理與程式設計,他們分佈在各種業務場景中。相比而言,幾乎每一個NoSQL開發人員都還處於學習階段。隨著時間的流逝,這種狀況將得到解決,但是,現在,尋找一個有經驗的RDBMS開發人員與RDBMS管理員要比尋找一個NoSQL專家要容易的多。

  結論

  NoSQL資料庫正在成為越來越多的資料庫環境的重要的組成部分,如果使用得當的話,它可以提供實實在在的收益。然而,企業在推進它們的使用時需要非常謹慎,需要明白這些資料庫的相關內在限制與問題。

NoSQL生態系統大檢閱 不同特性大比拼

雖然NoSQL很火熱,但是真正應用NoSQL的使用者不多。本文將為大家以對比的形式來介紹不同NoSQL產品的特點,希望對大家有所幫助。

  空前的資料量正在驅動商業尋找傳統關係型資料庫的替代方案,它已經為我們服務30多年了(今年5月份ACM剛剛給關係型資料慶祝40歲生日).總體來講,這些替代方案就是目前知名的“NoSQL資料庫.”

  關係型資料庫的基本問題是無法處理許多現代的工作負載.有三個具體的問題領域:向外擴充套件(Scaleout)類似於Digg(3TB的綠色徽章資料)或Facebook(50T的收件箱搜尋資料)或Ebay(總共2PB的資料)的資料集,單機效能限制以及僵化的概要設計.

  商業上(包含Rackspace Cloud公司)需要尋找新的方式來儲存並擴充套件大規模的資料.我最近寫了一篇關於Cassandra的文章,一個我們投入了資源的非關係型資料庫.還有另外一些正在運作中的非關係型資料庫,它們彙總在一起被我們稱為”NoSQL運動”.

  “NoSQL”這個術語實際上是由一個Rackspace的員工Eric Evans最先提出的,當時來自Last.fm網站的Johan Oskarsson提議組織一次開源分散式資料庫的研討會.這個名稱與概念就一起流行了起來.

  有些人反對NoSQL這個說法,因為它聽起來像是僅僅表明了我們不做什麼,而不是我們在做什麼.事實確實是這樣,我也基本同意此說法,但是這個術語仍然有其價值,因為當關系型資料庫是你所知道的唯一工具時,每個問題看起來都像個拇指(俗語,如果你手裡有一個錘子,你看到什麼都是釘子,譯者補充).NoSQL這個術語起碼讓人們知道還有其他的選項可供選擇.但是,當關系型資料庫是解決問題的最佳工具時,我們並不是反關係型資料庫者;它的涵義應該是“不僅僅有SQL(Not Only SQL)”而不是“不再有SQL(No SQL atall)”.

  有關NoSQL名稱的一個真實的憂慮是,它是如此大的一個概念,以致於差異巨大的設計都可以涵蓋其中.如果在討論各種產品時沒有搞清楚這一點,就會導致概念混亂.因此,我建議大家沿著下面三個維度來思考這些資料庫選項: 可伸縮性(scalability)、資料模型與查詢模型(data andquery model)以及持久化設計(persistencedesign).

  我選擇了10種NoSQL資料庫作為示例.這不是一份詳盡的清單,但是這裡討論的概念對於評估其他的NoSQL資料庫也至關重要.

  可伸縮性(Scalability)

  透過使用複製,就可以輕易擴充套件讀的規模,因此,每當我在此文中談到規模伸縮(scaling),都是表示透過自動分割槽將資料分佈到多臺機器以擴充套件寫的規模.我們將做這種事情的系統稱為“分散式資料庫”.它們包括Cassandra、HBase、Riak、Scalaris、Voldemort以及其他很多類似的系統.如果你的寫容量或寫資料大小已經無法在一臺機器上進行處理,如果你不想自己手工來管理分割槽的話,這些就是你的唯一選項了.(你不會這麼做吧?)

  人們使用分散式資料庫主要關注兩件事情: 1) 是否支援多個資料中心以及2) 能否在對應用透明的前提下往正在執行的叢集中新增新機器的能力.

  非分散式NoSQL資料庫包括CouchDB、MongoDB、Neo4j、Redis以及TokyoCabinet.它們可作為分散式系統的持久層;MongoDB提供了受限制的資料分片(Sharding)功能,CouchDB也有一個獨立的Lounge專案來支援做類似的分片功能,TokyoCabinet可用作Voldemort的儲存引擎.

  資料模型與查詢模型

  NoSQL資料庫之間的資料模型與查詢API千差萬別.

  部分重點內容介紹:

  Cassandra與HBase共同使用的ColumnFamily模型都是受到Google的Bigtable論文第2節的啟發.(Cassandra丟棄了歷史版本,並增加了超級列(SuperColumn)的概念).在這兩個系統中,都有與你之前看到的關係型資料庫類似的行/列概念,但是此處的行是稀疏的行:你想要一行有多少列,一行就可以有多少列,這些列並不需要事先定義好.

  鍵值(Key/value)模型是最簡單也最容易實現的模型,但是,如果你僅想對值(Value)的一部分進行查詢/更新時,它的效率會比較低.要想在一個分散式的鍵值上,實現更加複雜的結構也會非常困難.

  文件資料庫實際上是更高階的鍵/值(Key/Value)資料庫,允許在每個鍵上關聯巢狀的值.相對於每次簡單地返回整個BLOB(二進位制大物件)來講,文件資料庫支援更高效的查詢.

  Neo4j擁有一個非常獨特的資料模型,它以節點與邊的形式在圖中儲存物件與關係.對於適合這個模型(例如,分層資料)的查詢,它的效能可能會達到其替代選項的1000倍.

  Scalaris的獨特之處在於,它可以提供跨越多個鍵的分散式事務.(關於一致性與可用性的權衡的討論超出了本文的範圍,但是,在評估分散式系統時,它也是需要記住的一方面.)

  持久化設計

  關於持久化設計,我的意思是“資料在內部是如何儲存的?”

  持久化模型可以為我們提供大量關於這些資料庫適合處理多大工作負載的資訊.

  記憶體資料庫非常非常快(單臺機器上的Redis可以處理100,000次操作/秒),但是無法處理超過可用記憶體的資料集.永續性(Durability,資料不會由於伺服器崩潰或停電而丟失)也是個問題;在兩次重新整理到磁碟的時間間隔內預期資料丟失量可能非常大.Scalaris是我們此列表中唯一的記憶體資料庫,它透過複製來解決永續性的問題,但是,由於它不支援跨越多個資料中心,因此,如果遇到類似電源故障一類的問題資料仍將非常脆弱.

  在為了永續性寫入一個僅可追加的提交日誌之後,Memtable與SSTable會緩衝記憶體中的寫操作.在接受了足夠多的寫操作之後(Memtable達到一定的閾值),就會對memtable中的資料進行排序,並一次性寫入到磁碟,寫入的檔案就是一個“sstable.”這樣它就可以提供接近於記憶體處理的效能,因為它不涉及任何檢索操作,同時又可以避免純粹在記憶體中的方法那樣遭遇永續性問題.(在前面引用的Bigtable論文的第5.3與5.4兩節,以及論文日誌結構的合併樹(TheLog-Structuredmerge-tree)中對此都有詳細的描述)

  幾乎從有資料庫開始,B-樹就開始在資料庫中使用了.它們提供健壯的索引支援,但是在旋轉磁碟(仍然是目前最經濟實用的儲存介質)上, 它的效能表現比較差,因為它讀寫任何內容都會涉及到多次磁碟檢索.

  CouchDB的僅可做追加操作的B-樹(Append-OnlyB-tree)是一個比較有趣的變體,它以限制CouchDB併發寫(one write at a time)的代價避免了其檢索的開銷.

  結論

  NoSQL運動在2009年取得了爆發性的效果,因為越來越多的企業需要處理大規模的資料.Rackspace Cloud公司很高興在NoSQL運動扮演了一個較早期的角色,還會持續為Cassandra投入資源並支援與NoSQL East類似的活動.

NoSQL資料庫利弊分析:五大優勢五大挑戰

關聯式資料庫模型已經流行了幾十年了,但是一種新型別的資料庫——被稱為NoSQL,正在引起企業的注意。下面是關於它的優勢和劣勢的一個概述。二十多年以來,對資料庫管理來說,關聯式資料庫(RDBMS)模型一直是一個佔統治地位的資料庫模型。但是,今天,非關聯式資料庫,“雲”資料庫,或“NoSQL”資料庫作為關聯式資料庫以外的一些選擇,正在引起大家的廣泛關注。在這篇文章裡,我們將主要關注那些非關係的NoSQL資料庫的十大利弊:包括五大優勢和五大挑戰。

NoSQL的五大優勢

1.靈活的可擴充套件性

多年以來,資料庫管理員們都是透過“縱向擴充套件”的方式(當資料庫的負載增加的時候,購買更大型的伺服器來承載增加的負載)來進行擴充套件的,而不是透過“橫向擴充套件”的方式(當資料庫負載增加的時候,在多臺主機上分配增加的負載)來進行擴充套件。但是,隨著交易率和可用性需求的增加,資料庫也正在遷移到雲端或虛擬化環境中,“橫向擴充套件”在commodity hardware方面的經濟優勢變得更加明顯了,對各大企業來說,這種“誘惑”是無法抗拒的。

在commodity clusters上,要對RDBMS做“橫向擴充套件”,並不是很容易,但是各種新型別的NoSQL資料庫主要是為了進行透明的擴充套件,來利用新節點而設計的,而且,它們通常都是為了低成本的commodity hardware而設計的。

2.大資料

在過去的十年裡,正如交易率發生了翻天覆地的增長一樣,需要儲存的資料量也發生了急劇地膨脹。O’Reilly把這種現象稱為:“資料的工業革命”。為了滿足資料量增長的需要,RDBMS的容量也在日益增加,但是,對一些企業來說,隨著交易率的增加,單一資料庫需要管理的資料約束的數量也變得越來越讓人無法忍受了。現在,大量的“大資料”可以透過NoSQL系統(例如:Hadoop)來處理,它們能夠處理的資料量遠遠超出了最大型的RDBMS所能處理的極限。

3.“永別了”!DBA們!

在過去的幾年裡,雖然一些RDBMS供應商們聲稱在可管理性方面做出了很多的改進,但是高階的RDBMS系統維護起來仍然十分昂貴,而且還需要訓練有素的DBA們的協助。DBA們需要親自參與高階的RDBMS系統的設計,安裝和調優。

NoSQL資料庫從一開始就是為了降低管理方面的要求而設計的:從理論上來說,自動修復,資料分配和簡單的資料模型的確可以讓管理和調優方面的要求降低很多。但是,DBA的死期將至的謠言未免有些過於誇張了。總是需要有人對關鍵性的資料庫的效能和可用性負責的。

4.經濟

NoSQL資料庫通常使用廉價的commodity servers叢集來管理膨脹的資料和事務數量,而RDBMS通常需要依靠昂貴的專有伺服器和儲存系統來做到這一點。使用NoSQL,每GB的成本或每秒處理的事務的成本都比使用RDBMS的成本少很多倍,這可以讓你花費更低的成本儲存和處理更多的資料。

5.靈活的資料模型

對於大型的生產性的RDBMS來說,變更管理是一件很令人頭痛的事情。即使只對一個RDBMS的資料模型做了很小的改動,也必須要十分小心地管理,也許還需要停機或降低服務水平。NoSQL資料庫在資料模型約束方面是更加寬鬆的,甚至可以說並不存在資料模型約束。NoSQL的主鍵值資料庫和文件資料庫可以讓應用程式在一個資料元素裡儲存任何結構的資料。即使是規定更加嚴格的基於“大表”的NoSQL資料庫(例如:Cassandra, HBase)通常也允許建立新列,這並不會造成什麼麻煩。

應用程式變更和資料庫模式的變更並不需要作為一個複雜的變更單元來管理。從理論上來說,這可以讓應用程式迭代的更快,但是,很明顯,如果應用程式無法維護資料的完整性,那麼這會帶來一些不良的副作用。

NoSQL的五大挑戰

NoSQL的種種承諾引發了一場熱潮,但是在它們得到主流的企業的青睞以前,它們還有許多困難需要克服。下面是NoSQL需要面對的一些挑戰。

1.成熟度

RDBMS系統已經發展很長時間了。NoSQL的擁護者們認為,RDBMS系統那超長的發展的年限恰恰表示它們已經過時了,但是對於大多數的CIO們來說,RDBMS的成熟度更加令它們放心。大多數情況下,RDBMS系統更加穩定,而且功能也更加豐富。相比之下,大多數的NoSQL資料庫都是pre-production版本,許多關鍵性的功能還有待實現。

對於大多數開發者來說,處於技術的最前沿的確是很令人興奮的,但是企業應該懷著極端謹慎的態度來處理此事。

2.支援

企業都希望能得到這樣的保證:如果一個關鍵性的系統出現問題了,他們可以獲得及時有效的支援。所有的RDBMS供應商都在竭盡全力地提供高水平的企業支援。

相反,大多數的NoSQL系統都是開源專案,雖然對於每個NoSQL資料庫來說,通常也會有一個或多個公司對它們提供支援,但是那些公司通常是小型的創業公司,在支援的範圍,支援的資源,或可信度方面,它們和Oracle, Microsoft或IBM是無法相提並論的。

3.分析和商業智慧化

NoSQL資料庫現在已經可以滿足現代的Web2.0應用程式的高度的可擴充套件性的要求了。這直接導致的結果是,它們的大多數功能都是面向這些應用程式而設計的。但是,在一個應用程式中,具有商業價值的資料早就已經超出了一個標準的Web應用程式需要的“插入-讀取-更新-刪除”的範疇了。在公司的資料庫中進行商業資訊的挖掘可以提高企業的效率和競爭力,而且對於所有的中到大型的公司來說,商業智慧化(BI)一直是一個至關重要的IT問題。

NoSQL資料庫幾乎沒有提供什麼專用的查詢和分析工具。即使是一個簡單的查詢,也要求操作者具有很高超的程式設計技術,而且,常用的BI工具是無法連線到NoSQL的。

像HIVE或PIG那樣的新出現的一些解決方案在這方面可以提供一些幫助,它們可以讓訪問Hadoop叢集中的資料變得更加容易,最後也許還會支援其他的NoSQL資料庫。Quest軟體已經開發了一個產品——Toad for Cloud Databases——它給各種NoSQL資料庫提供了專用的查詢功能。

4.管理

NoSQL的設計目標是提供一個“零管理”的解決方案,但是目前來說,還遠遠沒有達到這個目標。安裝NoSQL還是需要很多技巧的,同時,維護它也需要付出很多的努力。

5.專業知識

毫不誇張地說,全世界有數百萬的開發者,他們都對RDBMS的概念和程式設計方法很熟悉,在每個業務部門中都有這樣的開發者。相反,幾乎每一個NoSQL開發者都正處於學習狀態中。雖然這種情況會隨著時間的推移而改變,但是現在,找到一些有經驗的RDBMS程式設計師或管理員要比找到一個NoSQL專家容易的多。

結論

NoSQL資料庫正在逐漸地成為資料庫領域中不可或缺的一部分,如果使用方法得當的話,能獲得很多的好處。但是,企業應該謹慎行事,要充分地認識到這些資料庫的一些限制和問題。

SQL or NoSQL——雲端計算環境中該選擇誰?

NoSQL和SQL之間真正的差異是什麼?實質上,是因為不同的訪問模式導致了NoSQL和SQL可擴充套件性和效能上的不同。

NoSQL只允許資料在受限的預定義模式訪問。例如DHT (Distributed Hash Table)透過hashtable API訪問。其他NoSQL資料服務訪問模式同樣受限。因此可擴充套件性和效能結構是可預測和可靠的。

而在SQL中,訪問模式預先是不知道的,SQL是一種通用語言,允許資料以各種方式訪問,程式設計師也對SQL語句的執行能力控制有限。

換句話說,在SQL中,資料模型不執行特定的工作方式與資料。強調建立資料完整性、簡潔性、標準化和抽象化。這對於所有大型複雜的應用極為重要。

為什麼是NoSQL

NoSQL提供的方法對於SQL資料庫來說有巨大的優勢。因為它允許應用程式擴充套件的新的水平。新的資料服務基於真正可擴充套件的結構和體系構建雲、構建分散式。這對於應用開發來說是非常有吸引力的。無需DBA,無需複雜的SQL查詢。

這是不小的問題,一個好程式設計師自由選擇一個資料模型,使用熟悉的工具寫應用程式,減少對他人的依賴於,並測試和最佳化的程式碼,而不做猜測或一個黑盒(DB)的計數。

這些都是NoSQL運動的所有主要優勢,但NoSQL也非萬能,具體而言,資料模型的選擇、介面規範以及當前面臨的新業務比如移動業務資料的處理問題,都是NoSQL無法迴避的。

NoSQL絕非萬能

資料模型

如果沒有一個統一的、定義良好的資料模型,無論採用何種技術都有缺陷。

SQL的資料模型定義了高度結構化的資料結構,以及對這些結構之間關係的嚴格定義。在這樣的資料模型上執行的查詢操作會比較侷限,而且可能會導致複雜的資料遍歷操作。但是資料結構的複雜性及查詢的複雜性,會導致系統產生如下的一些限制:比如當資料量增長到一臺機器已經不能容納,我們需要將不同的資料表分佈到不同的機器;如果你的結構化資料並沒有那麼強,或者對每一行資料的要求比較靈活,那可能關係型的資料模型就太過嚴格了;再有,使用簡單的查詢語言可能會導致應用層的邏輯更復雜,但是這樣可以將儲存系統的工作簡單化,讓它只需要響應一些簡單的請求。

此外,NoSQL資料庫並非是唯一適合儲存大量資料或大型資料,顯然,透過良好的分割槽設計,SQL資料庫也可以獲得極好的擴充套件性。

介面和互操作問題

不可否認,NoSQL的資料服務介面還有待規範。比如DHT,這是一個簡單的介面,但仍舊沒有標準的語義。每個DHT服務都使用其自己的一套介面。另一個大問題是不同的資料結構,如 DHT和binary tree,只是作為一個例子,共享資料物件。所有這些服務中,指標沒有內在的語義。事實上,這些服務中,處理互操作性是開發者的職責,這一點很很重要,尤其是當需要資料被多個服務訪問時。一個簡單的例子:後臺工作由Java實現,Web服務類工作由PHP實現,資料可以被輕易地從兩個域訪問資料嗎?顯然,人們可以使用Web服務作為前端資料訪問層,但是,NoSQL有可能讓事情變得更復雜,並降低了業務敏捷性,靈活性和效能,同時增加了開發工作量。

移動業務

在移動業務領域,需要一套工具,這套工具不僅要有可擴充套件性,而且還易於管理並且穩定,並在雲上有一個固定的設定伺服器。當系統出現問題的時候,可以不需要透過判斷整個系統或開發平臺來診斷問題,而是透過遠端訪問——這正是運維經理們所要面對的問題,但是在目前NoSQL所能提供的服務功能來看,很難實現,即便是Amazon的託管環境。

SQL和NoSQL如何結合?

總而言之,在NoSQL和SQL的選擇上,需要了解到以下內容:

資料模型及操作模型:你的應用層資料模型是行、物件還是文件型的呢?這個系統是否能支援你進行一些統計工作呢?

可靠性:當你更新資料時,新的資料是否立刻寫到持久化儲存中去了?新的資料是否同步到多臺機器上了?

擴充套件性:你的資料量有多大,單機是否能容下?你的讀寫量求單機是否能支援?

分割槽策略:考慮到你對擴充套件性,可用性或者永續性的要求,你是否需要一份資料被存在多臺機器上?你是否需要知道資料在哪臺機器上,以及你能否知道。

一致性:你的資料是否被複制到了多臺機器上,這些分佈在不同點的資料如何保證一致性?

事務機制:你的業務是否需要ACID的事務機制?

單機效能:如果你打算持久化的將資料存在磁碟上,哪種資料結構能滿足你的需求(你的需求是讀多還是寫多)?寫操作是否會成為磁碟瓶頸?

負載可評估:對於一個讀多寫少的應用,諸如響應使用者請求的web應用,我們總會花很多精力來關注負載情況。你可能需要進行資料規模的監控,對多個使用者的資料進行彙總統計。你的應用場景是否需要這樣的功能呢?

使用NoSQL架構實現SQL資料庫?

使用NoSQL的基礎架構實現SQL資料庫是一個很好的解決方案。一個SQL資料庫是可擴充套件、易管理,雲就緒、高度可用的,完全建立在NoSQL的基礎結構(分散式)上,但仍然提供SQL資料庫的所有優勢,如互操作性,定義良好的語義以及更多。

這種混合結構也許不如純粹的NoSQL的服務,但足以滿足需要更穩定系統、可擴充套件性和雲服務的80%的市場需求。

這種解決辦法還允許很容易地遷移現有的應用到雲環境,從而保護相關組織在這些應用上所付出的巨大的投資。

在我看來,構建於NoSQL基礎之上的SQL資料庫,可以為那些在其成長期間期望靈活、高效的客戶提供最高的價值。

NoSQL架構實踐(一)——以NoSQL為輔

怎麼樣把NoSQL引入到我們的系統架構設計中,需要根據我們系統的業務場景來分析,什麼樣型別的資料適合儲存在NoSQL資料庫中,什麼樣型別的資料必須使用關聯式資料庫儲存。明確引入的NoSQL資料庫帶給系統的作用,它能解決什麼問題,以及可能帶來的新的問題。下面我們分析幾種常見的NoSQL架構。

(一)NoSQL作為映象

不改變原有的以MySQL作為儲存的架構,使用NoSQL作為輔助映象儲存,用NoSQL的優勢輔助提升效能。

圖 1 -NoSQL為映象(程式碼完成模式 )

//寫入資料的示例虛擬碼 //data為我們要儲存的資料物件 data.title=”title”; data.name=”name”; data.time=”2009-12-01 10:10:01”; data.from=”1”; id=DB.Insert(data);//寫入MySQL資料庫 NoSQL.Add(id,data);//以寫入MySQL產生的自增id為主鍵寫入NoSQL資料庫

如果有資料一致性要求,可以像如下的方式使用

//寫入資料的示例虛擬碼 //data為我們要儲存的資料物件 bool status=false; DB.startTransaction();//開始事務 id=DB.Insert(data);//寫入MySQL資料庫 if(id>0){ status=NoSQL.Add(id,data);//以寫入MySQL產生的自增id為主鍵寫入NoSQL資料庫 } if(id>0 && status==true){ DB.commit();//提交事務 }else{ DB.rollback();//不成功,進行回滾 }

上面的程式碼看起來可能覺得有點麻煩,但是隻需要在DB類或者ORM層做一個統一的封裝,就能實現重用了,其他程式碼都不用做任何的修改。

這種架構在原有基於MySQL資料庫的架構上增加了一層輔助的NoSQL儲存,程式碼量不大,技術難度小,卻在可擴充套件性和效能上起到了非常大的作用。只需要程式在寫入MySQL資料庫後,同時寫入到NoSQL資料庫,讓MySQL和NoSQL擁有相同的映象資料,在某些可以根據主鍵查詢的地方,使用高效的NoSQL資料庫查詢,這樣就節省了MySQL的查詢,用NoSQL的高效能來抵擋這些查詢。

圖 2 -NoSQL為映象(同步模式)

這種不透過程式程式碼,而是透過MySQL把資料同步到NoSQL中,這種模式是上面一種的變體,是一種對寫入透明但是具有更高技術難度一種模式。這種模式適用於現有的比較複雜的老系統,透過修改程式碼不易實現,可能引起新的問題。同時也適用於需要把資料同步到多種型別的儲存中。

MySQL到NoSQL同步的實現可以使用MySQL UDF函式,MySQL binlog的解析來實現。可以利用現有的開源專案來實現,比如:

有了這兩個MySQL UDF函式庫,我們就能透過MySQL透明的處理Memcached或者Http協議,這樣只要有相容Memcached或者Http協議的NoSQL資料庫,那麼我們就能透過MySQL去操作以進行同步資料。再結合,透過UDF和MySQL觸發器功能的結合,就可以實現資料的自動同步。

(二)MySQL和NoSQL組合

MySQL中只儲存需要查詢的小欄位,NoSQL儲存所有資料。

圖 3 -MySQL和NoSQL組合

//寫入資料的示例虛擬碼 //data為我們要儲存的資料物件 data.title=”title”; data.name=”name”; data.time=”2009-12-01 10:10:01”; data.from=”1”; bool status=false; DB.startTransaction();//開始事務 id=DB.Insert(“INSERT INTO table (from) VALUES(data.from)”);//寫入MySQL資料庫,只寫from需要where查詢的欄位 if(id>0){ status=NoSQL.Add(id,data);//以寫入MySQL產生的自增id為主鍵寫入NoSQL資料庫 } if(id>0 && status==true){ DB.commit();//提交事務 }else{ DB.rollback();//不成功,進行回滾 }

把需要查詢的欄位,一般都是數字,時間等型別的小欄位儲存於MySQL中,根據查詢建立相應的索引,其他不需要的欄位,包括大文字欄位都儲存在NoSQL中。在查詢的時候,我們先從MySQL中查詢出資料的主鍵,然後從NoSQL中直接取出對應的資料即可。

這種架構模式把MySQL和NoSQL的作用進行了融合,各司其職,讓MySQL專門負責處理擅長的關係儲存,NoSQL作為資料的儲存。它有以下優點:

  • 節省MySQL的IO開銷。由於MySQL只儲存需要查詢的小欄位,不再負責儲存大文字欄位,這樣就可以節省MySQL儲存的空間開銷,從而節省MySQL的磁碟IO。我們曾經透過這種最佳化,把MySQL一個40G的表縮減到幾百M。
  • 提高MySQl Query Cache快取命中率。我們知道query cache快取失效是表級的,在MySQL表一旦被更新就會失效,經過這種欄位的分離,更新的欄位如果不是儲存在MySQL中,那麼對query cache就沒有任何影響。而NoSQL的Cache往往都是行級別的,只對更新的記錄的快取失效。
  • 提升MySQL主從同步效率。由於MySQL儲存空間的減小,同步的資料記錄也減小了,而部分資料的更新落在NoSQL而不是MySQL,這樣也減少了MySQL資料需要同步的次數。
  • 提高MySQL資料備份和恢復的速度。由於MySQL資料庫儲存的資料的減小,很容易看到資料備份和恢復的速度也將極大的提高。
  • 比以前更容易擴充套件。NoSQL天生就容易擴充套件。經過這種最佳化,MySQL效能也得到提高。

MySQL與NoSQL——SQL與NoSQL的融合

寫這一篇內容的原因是MySQL5.6.2突然推出了memcached的功能。NoSQL to InnoDB with Memcached的出現,可以看出NoSQL對關聯式資料庫的確產生了巨大的影響,個人覺得這是一個非常大的進步,可以讓開發人員更加方便的使用NoSQL和關聯式資料庫。NoSQL一般被認為效能高於關聯式資料庫,那麼直接在InnoDB之上提供NoSQL功能並和MySQL共存是否是一個更好的選擇呢?

MySQL withHandlerSocket

去年在twitter上看到的出現,並宣稱效能是Memcached的兩倍時,非常令人吃驚,居然可以達到750000qps。接著HandlerSocket成為NoSQL領域談論的焦點之一, 大量的人開始想要嘗試,並做過一些自己的效能測試。 下圖是HandlerSocket的結構圖:

圖1 HandlerSocket結構圖(來源於官方)

HandlerSocket的出現,給我們眼前一亮的感覺。原來InnoDB的效能已經足夠好,並可以直接提供NoSQL的功能。最大的好處就是可以共享MySQL的功能,DBA以前的經驗一樣可以用。但是有些小小的風險:

?HandlerSocket沒有與MySQL一起釋出版本,因此對於使用MyISAM引擎的使用者是無緣的。不過現在Percona-Server已經整合了HandlerSocket,可以非常方便的使用。

?目前大規模的成功案例並不多,國內也只有少部分公司在嘗試,我知道的有飛信開放平臺,據說還不錯。

?官方給出的測試資料在應用場景上其實並不充分,至少測試的場景跟我們實際使用的場景相差很大。但是毫無疑問, HandlerSocket的效能比直接使用MySQL肯定要高效得多。

InnoDB with Memcached

也許是因為HandlerSocket的火爆的衝擊,也許是受HandlerSocket的啟發,MySQL開始關注NoSQL領域的應用,並在MySQL5.6.2版本增加了透過Memcached協議直接訪問原生Innodb API的功能。

InnoDB with Memcached是在提供MySQL服務的同一程式中提供Memcached服務 ,這與HandlerSocket的架構模式幾乎是一樣的。雖然目前InnoDB with Memcached還是預覽版本,但是我個人更看好它,因為:

?它使用Memcached協議,並同時支援文字和二進位制協議,在client的選擇和成熟度上就要勝出許多;

?其支援的三種cache模式,不但可以省去開發中使用Memcached來快取資料的麻煩,並且具有更好的可靠性和資料一致性;

?在應用程式中,可以使用高效的memcached協議來運算元據,同時也可以使用sql進行復雜的查詢操作;

注意:目前透過memcached的更新操作不會記錄到binlog中,未來的版本會支援。

圖二 InnoDB withMemcached

Memcached and MySQLCluster

顯而易見,我們會想到MySQL Cluster結合Memcached是一個更好的組合,MySQL Cluster提供了99.999%高可用性,並真正提供了去中心化的無縫高可擴充套件性。還有什麼比這更人興奮的呢。

MySQL已經提供了這樣的功能,原始碼在。這裡有一個O'Reilly MySqlConference大會的 ,你也可以看下這個功能開發者的一篇部落格。

圖三 NDB with Memcached

MySQL Cluster雖然具有高可靠性和無縫擴充套件的優勢,但是對於複雜SQL查詢的效率卻不能令人滿意。不過對於僅僅依賴於key-value查詢和寫入的海量資料儲存需求,MySQL Cluster with Memcached應該是個很好的選擇。

總結

Memcached協議由於其簡單、協議輕量、存在大量的client,所以提供相容Memcached協議的產品比較佔據先天的優勢。

MySQL提供NoSQL的功能,個人覺得並不是MySQL耐不住寂寞,而是的確在響應使用者的需求。我前面的文章也說過,“NoSQL只是一個概念,並不是一個資料庫 產品,MySQL也可以是NoSQL”,現在也正應了這句話。NoSQL從架構上就約束了開發者的架構和開發方式,從而提高擴充套件性和效能,而NoSQL和MySQL的融合,也同時提供了複雜查詢功能。

雖然MySQL提供了NoSQL功能,如果你要嘗試的話,你的資料庫設計必須從NoSQL從發,然後再考慮SQL查詢功能。

SQL與NoSQL的融合的確會給開發者帶來方便,比如最近很流行的Mongodb,它吸引開發最大的點就是支援簡單的關係查詢。SQL與NoSQL的融合可能是未來很多資料庫產品的一個趨勢。但是純NoSQL資料庫的優勢也是顯著的,就是他的簡單、高效、易擴充套件。

 

解讀NoSQL技術代表之作Dynamo

NoSQL背後的兩種模式

NoSQL其實並不是什麼妖魔鬼怪,相反,NoSQL的真諦其實應該是Not Only SQL,其產生背景是在資料量和訪問量逐漸增大的情況下下,人為地去新增機器或者切分資料到不同的機器,變得越來越困難,人力成本越來越高,於是便開始有了這樣的專案,它們的本意是提高資料儲存的自動化程度,減少人為干預的時間,讓負載更加均勻等。在國際上,真正的代表之作有來自Google的 BigTable 和Amazon的Dynamo,他們分別使用了不同的基本原理。

MapReduce

這是歷史最久的一種模型,典型的代表是BigTable。Map表示對映,Reduce表示化簡。MapReduce透過把對資料集的大規模操作分發給網路上的每個節點實現可靠性(Map);每個節點會週期性地把完成的工作和狀態的更新報告回來(Reduce)。大多數分散式運算可以抽象為MapReduce操作。Map是把輸入Input分解成中間的Key/Value對,Reduce把Key/Value合成最終輸出Output。這兩個函式由程式設計師提供給系統,下層設施把Map和Reduce操作分佈在叢集上執行。

Dynamo

這裡我把Dynamo專門歸納成為了一種,其原因是它與MapReduce有很大的不同,自成一派。先說一下歷史,Amazon於2006年推出了自己的雲端儲存服務S3,2007年其CTO公佈了S3的設計方案,從此江湖中就不再太平了,開源專案一個個如雨後春筍般地出現了。比較常見的有Facebook開發的Cassandra(如果沒有記錯,在去年瀏覽他們專案網頁的時候,上面還寫著他們之中的一個開發人員是Dynamo的設計人員,現在風頭緊,去掉了),還有Linkedin的voldemort,而在國內話,有豆瓣網的beansDB,人人網的nuclear等等。這裡我主要討論的也是Dynamo的方案細節。

入門基礎

Dynamo的意思是發電機,顧名思義,這一整套的方案都像發電機一樣,源源不斷地提供服務,永不間斷。以下內容看上去有點教條,但基本上如果你要理解原理,這每一項都是必須知道的。

CAP原則

先來看歷史,Eric A. Brewer教授,Inktomi公司的創始人,也是berkeley大學的計算機教授,Inktomi是雅虎搜尋現在的臺端技術核心支援。最主要的是,他們(Inktomi公司)在最早的時間裡,開始研究分佈計算。CAP原則的提出,可以追溯到2000年的時候(可以想象有多麼早!),Brewer教授在一次談話中,基於他運作Inktomi以及在伯克利大學裡的經驗,總結出了CAP原則(文末參考資料中有其演講資料連結)。圖一是來自Brewer教授當年所畫的圖:

圖一:CAP原則當年的PPT

Consistency(一致性):即資料一致性,簡單的說,就是資料複製到了N臺機器,如果有更新,要N機器的資料是一起更新的。
Availability(可用性):好的響應效能,此項意思主要就是速度。
Partition tolerance(分割槽容錯性):這裡是說好的分割槽方法,體現具體一點,簡單地可理解為是節點的可擴充套件性。

定理:任何分散式系統只可同時滿足二點,沒法三者兼顧。
忠告:架構師不要將精力浪費在如何設計能滿足三者的完美分散式系統,而是應該進行取捨。

DHT——分散式雜湊表

DHT(Distributed Hash Table,分散式雜湊表),它是一種分散式儲存定址方法的統稱。就像普通的雜湊表,裡面儲存了key與value的對應關係,一般都能根據一個key去對應到相應的節點,從而得到相對應的value。

這裡隨帶一提,在DHT演算法中,一致性雜湊作為第一個實用的演算法,在大多數系統中都使用了它。一致性雜湊基本解決了在P2P環境中最為關鍵的問題——如何在動態的網路拓撲中分佈儲存和路由。每個節點僅需維護少量相鄰節點的資訊,並且在節點加入/退出系統時,僅有相關的少量節點參與到拓撲的維護中。至於一致性雜湊的細節就不在這裡詳細說了,要指明的一點是,在Dynamo的資料分割槽方式之後,其實內部已然是一個對一致性雜湊的改造了。

進入Dynamo的世界

有了上面一章裡的兩個基礎介紹之後,我們開始進入Dynamo的世界。

Dynamo的資料分割槽與作用

在Dynamo的實現中提到一個關鍵的東西,就是資料分割槽。 假設我們的資料的key的範圍是0到2的64次方(不用懷疑你的資料量會超過它,正常甚至變態情況下你都是超不過的,甚至像伏地魔等其他類Dynamo系統是使用的 2的32次方),然後設定一個常數,比如說1000,將我們的key的範圍分成1000份。然後再將這1000份key的範圍均勻分配到所有的節點(s個節點),這樣每個節點負責的分割槽數就是1000/s份分割槽。

如圖二,假設我們有A、B、C三臺機器,然後將我們的分割槽定義了12個。

圖二:三個節點分12個區的資料的情況

因為資料是均勻離散到這個環上的(有人開始會認為資料的key是從1、2、3、4……這樣子一直下去的,其實不是的,雜湊計算出來的值,都是一個離散的結果),所以我們每個分割槽的資料量是大致相等的。從圖上我們可以得出,每臺機器都分到了三個分割槽裡的資料,並且因為分割槽是均勻的,在分割槽數量是相當大的時候,資料的分佈會更加的均勻,與此同時,負載也被均勻地分開了(當然了,如果硬要說你的負載還是隻集中在一個分割槽裡,那就不是在這裡要討論的問題了,有可能是你的雜湊函式是不是有什麼樣的問題了)。

為什麼要進行這樣的分佈呢,分佈的好處在於,在有新機器加入的時候,只需要替換原有分割槽即可,如圖三所示:

圖三:加入一個新的節點D的情況

同樣是圖二里的情況,12個分割槽分到ABC三個節點,圖三中就是再進入了一個新的節點D,從圖上的重新分佈情況可以得出,所有節點裡只需要轉移四分之一的資料到新來的節點即可,同時,新節點的負載也伴隨分割槽的轉移而轉移了(這裡的12個分割槽太少了,如果是1200個分割槽甚至是12000個分割槽的話,這個結論就是正確的了,12個分割槽只為演示用)。

從Dynamo的NRW看CAP法則

在Dynamo系統中,第一次提出來了NRW的方法。
N:複製的次數;
R:讀資料的最小節點數;
W:寫成功的最小分割槽數。
這三個數的具體作用是用來靈活地調整Dynamo系統的可用性與一致性。

舉個例子來說,如果R=1的話,表示最少只需要去一個節點讀資料即可,讀到即返回,這時是可用性是很高的,但並不能保證資料的一致性,如果說W同時為1的 話,那可用性更新是最高的一種情況,但這時完全不能保障資料的一致性,因為在可供複製的N個節點裡,只需要寫成功一次就返回了,也就意味著,有可能在讀的這一次並沒有真正讀到需要的資料(一致性相當的不好)。如果W=R=N=3的話,也就是說,每次寫的時候,都保證所有要複製的點都寫成功,讀的時候也是都讀到,這樣子讀出來的資料一定是正確的,但是其效能大打折扣,也就是說,資料的一致性非常的高,但系統的可用性卻非常低了。如果R + W > N能夠保證我們“讀我們所寫”,Dynamo推薦使用322的組合。

Dynamo系統的資料分割槽讓整個網路的可擴充套件性其實是一個固定值(你分了多少區,實際上網路裡擴充套件節點的上限就是這個數),透過NRW來達到另外兩個方 向上的調整。

Dynamo的一些增加可用性的補救

針對一些經常可能出現的問題,Dynamo還提供了一些解決的方法。

第一個是hinted handoff資料的加入:在一個節點出現臨時性故障時,資料會自動進入列表中的下一個節點進行寫操作,並標記為handoff資料,在收到通知需要原節點恢復時重新把資料推回去。這能使系統的寫入成功大大提升。

第二個是向量時鐘來做版本控制:用一個向量(比如說[a,1]表示這個資料在a節點第一次寫入)來標記資料的版本,這樣在有版本衝突的時候,可以追溯到出現問題的地方。這可以使資料的最終一致成為可能。(Cassandra未用vector clock,而只用client timestamps也達到了同樣效果。)

第三個是Merkle tree來提速資料變動時的查詢:使用Merkle tree為資料建立索引,只要任意資料有變動,都將快速反饋出來。

第四個是Gossip協議:一種通訊協議,目標是讓節點與節點之間通訊,省略中心節點的存在,使網路達到去中心化。提高系統的可用性。

後記

Dynamo的理論對CAP原則裡的可擴充套件性做到了很方便的實現,透過創造性的NRW來平衡系統的可用性和一致性,增加了系統在實際情況下遇到問題的可選擇方案。可以相像,在NoSQL的道路上,這只是個開端,在分散式計算的道路上,已經是MapReduce之後的再次革命。

什麼是NoSQL——快速回顧

像許多關注這一領域的人一樣,我不喜歡從本質上將SQL與NoSQL這一術語對立起來。同時我對該術語現有的解釋"Not Only SQL"也不甚滿意。對我來說,我們這裡所討論的並非是是否使用SQL。(相反的是,我們仍然可以選擇類似SQL這樣的查詢介面(缺少對join等的支援)來與這些資料庫互動,使用現有的資源和技術來管理開發伸縮性和可維護性。) 這一運動是要找到儲存和檢索資料的其他高效的途徑,而不是盲目地在任何情況下都把關聯式資料庫當作萬金油。因此,我認為'Non Relational Database'(非關係型資料庫)能夠更好的表達這一思想。

無論採用哪個名字,“非關係型資料庫”這一範圍所傳達出來的“囊括所有”型別的意味,使得這一概念比較模糊(並且它還是否定型的)。這又使得人們(特別是企業中的決策者)對於哪些是屬於這個範圍,哪些不是,更重要的是,對他們來說這到底意味著什麼,感到非常迷惑。

為了解答這些疑問,我嘗試透過以下幾點特徵的描述,來刻畫“非關係型資料庫”的內在本質。

所謂“非關係型資料庫”指的是

  1. 使用松耦合型別、可擴充套件的資料模式來對資料進行邏輯建模(Map,列,文件,圖表等),而不是使用固定的關係模式元組來構建資料模型。
  2. 以遵循於(能保證在一致性,可用性和分割槽容忍性三者中中達到任意兩個)的跨多節點資料分佈模型而設計,支援水平伸縮。這意味著對於多資料中心和動態供應(在生產叢集中透明地加入/刪除節點)的必要支援,也即彈性(Elasticity)。
  3. 擁有在磁碟或記憶體中,或者在這兩者中都有的,對資料持久化的能力,有時候還可以使用可熱插拔的定製儲存。
  4. 支援多種的'Non-SQL'介面(通常多於一種)來進行資料訪問。

圍繞著圖中四個特徵的(資料永續性、邏輯資料模型、資料分佈模型和介面)“非關係型資料庫”的各種變形,在最近的一些文章中有詳盡的描述,並且在因特網上有著廣泛的傳播。所以我就不做過多繁複的描述,而是透過一些例子對關鍵的方向進行總結,供快速參考:

介面——REST (HBase,CouchDB,Riak等),MapReduce(HBase,CouchDB,MongoDB,Hypertable等),Get/Put(Voldemort,Scalaris等),Thrift (HBase,Hypertable,Cassandra等),語言特定的API(MongoDB)。

邏輯資料模型——面向鍵值對的(Voldemort,Dynomite等),面向Column Family的(BigTable,HBase,Hypertable等),面向文件的(Couch DB,MongoDB等),面向圖的(Neo4j, Infogrid等)

資料分佈模型——一致性和可用性(HBase,Hypertable, MongoDB等), 可用性和可分割槽性(Cassandra等)。一致性和可分割槽性的組合會導致一些非額定的節點產生可用性的損失。有趣的是目前還沒有一個“非關係型資料庫”支援這一組合。

資料永續性——基於記憶體的(如Redis,Scalaris, Terrastore),基於磁碟的(如MongoDB,Riak等),或記憶體及磁碟二者的結合(如HBase,Hypertable,Cassandra)。儲存的型別有助於我們辨別該解決方案適用於哪種型別。然而,在大多數情況下人們發現基於組合方案的解決方案是最佳的選擇。既能透過記憶體資料儲存支援高效能,又能在寫入足夠多的資料後儲存到磁碟來保證持續性。

如何將其與企業IT融合

如今的企業中,並非所有用例都直觀地傾向於使用關係型資料庫,或者都需要嚴格的ACID屬性(特別是一致性和隔離性)。在80年代及90年代,絕大部分儲存在企業資料庫裡的資料都是結構化的業務事務的“記錄”,必須用受控的方式來生成或訪問,而如今它已一去不復返了。無可爭辯的是,仍有這一型別的資料在那裡,並將繼續也應該透過關係型資料庫來建模,儲存和訪問。但對於過去15年以來,隨著Web的發展,電子商務和社交計算的興起所引起的企業裡不受控的非結構化並且面向資訊的資料大爆炸,該如何應對呢?企業確實不需要關係型資料庫來管理這些資料,因為關係型資料庫的特點決定了它不適用於這些資料的性質和使用方式。

上圖總結了現今以web為中心的企業中資訊管理的新興模式。而“非關係型資料庫” 是處理這些趨勢的最佳選擇(較之關係型資料庫來說),提供了對非結構化資料的支援,擁有支援分割槽的水平伸縮性,支援高可用性等等。

以下是支援這一觀點的一些實際應用場景:

日誌挖掘——叢集裡的多個節點都會產生伺服器日誌、應用程式日誌和使用者活動日誌等。對於解決生產環境中的問題,日誌挖掘工具非常有用,它能訪問跨伺服器的日誌記錄,將它們關聯起來並進行分析。使用“非關係型資料庫”來定製這樣的解決方案將會非常容易。

分析社交計算——許多企業如今都為使用者(內部使用者、客戶和合作夥伴)提供透過訊息論壇,部落格等方式來進行社交計算的能力。挖掘這些非結構化的資料對於獲得使用者的喜好偏向以及進一步提升服務有著至關重要的作用。使用“非關係型資料庫” 可以很好的解決這一需求。

外部資料feed聚合——許多情況下企業需要消費來自合作伙伴的資料。顯然,就算經過了多輪的討論和協商,企業對於來自合作伙伴的資料的格式仍然沒有發言權。同時,許多情況下,基於合作伙伴業務的變更,這些資料格式也頻繁的發生變化。透過“非關係型資料庫”來開發或定製一個ETL解決方案能夠非常成功的解決這一問題。

高容量的EAI系統——許多企業的EAI系統都有高容量傳輸流(不管是基於產品的還是定製開發的)。出於可靠性和審計的目的,這些透過EAI系統的訊息流通常都需要持久化。對於這一場景,“非關係型資料庫” 再次體現出它十分適用於底層的資料儲存,只要能給定環境中源系統和目標系統的資料結構更改和所需的容量。

前端訂單處理系統——隨著電子商務的膨脹,透過不同渠道流經零售商、銀行和保險供應商、娛樂服務供應商、物流供應商等等的訂單、應用、服務請求的容量十分巨大。同時,由於不同渠道的所關聯的行為模式的限制,每種情況下系統所使用的資訊結構都有所差異,需要加上不同的規則型別。在此之上,絕大部分資料不需要即時的處理和後端對帳。所需要的是,當終端使用者想要從任何地方推送這些資料時,這些請求都能夠被捕獲並且不會被打斷。隨後,通常會有一個對帳系統將其更新到真正的後端源系統並更新終端使用者的訂單狀態。這又是一個可以應用“非關係型資料庫”的場景,可用於初期儲存終端使用者的輸入。這一場景是體現“非關係型資料庫”的應用的極佳例子,它具有高容量,異構的輸入資料型別和對帳的"最終一致性"等等特點。

企業內容管理服務——出於各種各樣的目的,內容管理在企業內部得到了廣泛的應用,橫跨多個不同的功能部門比如銷售、市場、零售和人力資源等。企業大多數時間所面臨的挑戰是用一個公共的內容管理服務平臺,將不同部門的需求整合到一起,而它們的後設資料是各不相同的。這又是“非關係型資料庫”發揮作用的地方。

合併和收購——企業在合併與收購中面臨巨大的挑戰,因為他們需要將適應於相同功能的系統整合起來。“非關係型資料庫” 可解決這一問題,不管是快速地組成一個臨時的公共資料儲存,或者是架構一個未來的資料儲存來調和合並的公司之間現有公共應用程式的結構。

但我們如何才能準確的描述,相對於傳統的關係型資料庫,企業使用“非關係型資料庫”帶來的好處呢?下面是可透過非關係型資料庫的核心特點(正如上一節所討論的)而獲得的一些主要的好處,即企業的任何IT決策都會參考的核心引數——成本削減,更好的週轉時間和更優良的質量。

業務靈活性——更短的週轉時間

“非關係型資料庫”能夠以兩種基本的方式帶來業務靈活性。

  • 模式自由的邏輯資料模型有助於在為任何業務進行調整時帶來更快的週轉時間,把對現有應用和功能造成影響減到最少。在大多數情況下因任意的變更而給你帶來的遷移工作幾乎為零。
  • 水平伸縮效能夠在當越來越多的使用者負載造成負載週期性變化,或者應用突然變更的使用模式時,提供堅固的保障。面向水平伸縮性的架構也是邁向基於SLA構建(例如雲)的第一步,這樣才能保證在不斷變化的使用情形下業務的延續性。

更佳的終端使用者體驗——更優越的質量

在現今企業IT中,應用的質量主要由終端使用者的滿意度來決定。“非關係型資料庫”透過解決如下終端使用者的考慮因素,能夠達到同樣的效果,而這些因素也是最容易發生和最難以處理的。

  • “非關係型資料庫” 為提升應用的效能帶來了極大的機會。分散式資料的核心概念是保證磁碟I/O(尋道速率)絕不能成為應用效能的瓶頸。儘管效能更多的是由傳輸速率來決定。在此之上,絕大部分解決方案支援各種不同的新一代的高速計算的正規化,比如MapReduce,排序列,Bloom Filter,僅可追加的B樹,Memtable等。
  • 現今使用者滿意度的另一個重要的方面就是可靠性。終端使用者希望在想要訪問應用時就能訪問到,並且至少是在當他們分配到時間的時候能隨時執行他們的任務。所以不可用的應用需要不惜代價的避免。許多現代的“非關係型資料庫”都能適應並支援這一類有著嚴格和的可用性的需求。

更低的所有者總成本

在如今的競爭市場中,企業IT支出隨時都要仔細審查,以合理的成本獲取合理的質量才值得讚許。在這一領域中“非關係型資料庫”在一定程度上勝於傳統的資料庫,特別是當儲存和處理的資料容量很大時。

  • 水平伸縮性的基本前提保證了它們可以執行於廉價機器之上。這不僅削減了硬體資本的成本,同時還削減了諸如電力,維護等運維成本。同時這還進一步的為利用諸如雲、虛擬資料中心等下一代低成本的基礎設施打下了基礎。
  • 從長期來看,更少的維護能帶來更多的運維成本優勢。對於關係型資料庫,這絕對是一個需要儲存大容量資料的場景。為大容量的資料調優資料庫需要高超的技藝,也就意味著更高的成本。相較之下,“非關係型資料庫”始終提供快速和響應的特點,就算是在資料大幅上升的情況下。索引和快取也以同樣的方式工作。開發者不必過多擔心硬體、磁碟、重新索引及檔案佈局等,而是把更多的精力投入了應用程式的開發上。

企業採用中所面臨的挑戰

拋開所有這些長遠的好處,在企業擁抱“非關係型資料庫”之前,當然還需要經歷各種各樣的挑戰。

不考慮因現有思想的轉換和缺乏信心而產生的來自高層的阻力,目前我認為的最主要的戰術性挑戰是:

為“非關係型資料庫”認定正確的應用/使用場景

儘管從理論上容易論證並非所有的企業資料都需要基於關係和ACID的系統,然而由於關係型資料庫與企業資料間多年的繫結關係,要作出所有的資料可以透過非關係的解決方案而解耦的決定仍然有很多困難。許多時候IT經理(以及其它對於應用程式負有核心的底線責任的各級人員)不明白他們將會失去什麼,這樣的擔憂對於從關係型資料庫轉變出來比較不利。企業IT最有價值的資產就是資料。因此,要作出決定使用一種不太明確或者未被廣泛採用的解決方案來管理同樣的資料,這種能力不僅需要轉換思維方式,同時還需要來自高層的強大的支援(和推動)。

我們如何選擇最適合我們的產品/解決方案

另一個重大的挑戰是找出合適的產品/工具來提供“非關係型資料庫”。正如前面所提到的那樣,現今業界裡面有多於25種不同的產品和解決方案,它們在四個方面有著不同的特點。正因為每個產品在這四個方面特點各異,所以要選擇一個產品來應對所有的需求顯得尤為困難。有的時候,可能在企業的不同部門使用到多種型別的非關係型資料庫,最後人們可能會完全出於對標準的需要而轉向關係型資料庫。

如何獲得規模經濟

這一想法本質上是從前一個問題分支出來的。如果一個組織需要使用多個非關係型資料庫解決方案(由於單個方案的適用問題),那麼保證在技術(開發者,管理者,支援人員),基礎設施(硬體成本,軟體許可成本,支援成本,諮詢成本),以及工件(公共元件和服務)方面的規模經濟就是一個大問題。這一方面與傳統的關係型資料庫解決方案比較起來確實更為嚴峻,因為大部分時間組織的資料儲存都是以共享服務的模式在執行的。

我們如何保證解決方案的可移植性

從“非關係型資料庫”的發展來看,我們可以很直觀地推測在未來的幾年中這一領域會有許多變化,比如供應商的合併,功能的進步以及標準化。所以對於企業來說一個更好的策略是不要把寶押在某個特定的產品/解決方案上,以後才可以更靈活的轉換到一個更好的經過考驗的產品。 由於現在的非關係型產品/解決方案大部分是私有的,因此IT決策者在考慮嘗試“非關係型資料庫”之前,不得不認真考慮可移植性這一重要的問題。這純粹是出於保護現有投資的需要。

我們如何獲得合適的產品支援型別

現在的“非關係型資料庫”能透過外部組織而提供支援方案的少之又少。就算有,也無法與Oracle,IBM或者微軟等相比。特別是在資料恢復,備份和特定的資料恢復方面,由於許多“非關係型資料庫”在這些方面未能提供一個健壯而易於使用的機制,對於企業決策者來說,仍存在很大的問題。

我們如何預算整體成本

與重量級的關係型資料庫相比,“非關係型資料庫”通常在效能和伸縮性特徵方面能提供的資料更少。我也沒有發現有TPC基準程式方面和類似的其它方面的資料。這將企業決策者置於了一個“沒有方向”的情況下,因為他們不知道需要在硬體、軟體許可、基礎設施管理和支援等方面支出多大的費用。要得出一個預算估計,缺乏判斷的資料就成了一個主要的障礙。因此在專案啟動階段,大部分情況下決策者還是會選擇基於熟悉的關係型資料庫的解決方案。

有時候,就算可以得到這些數字,但也不足以用來形成TCO模型並與傳統的基於關係型資料庫的資料儲存和非關係型資料儲存進行整體的成本分析(Capex+Opex)比較。通常情況下水平伸縮性所要求的大量的硬體機器(以及軟體許可成本,支援成本),如果與垂直伸縮性乍一比較,會讓人覺得戰戰兢兢,除非由此帶來的好處經過基於TCO模型的全方位比較仍然被證明是可以持續的。

關於如何採用NoSQL的兩點思考

這是否意味著目前來看企業應該對NoSQL運動持觀望的態度呢?並非如此。誠然,“非關係型資料庫”對於廣泛的採用來說還未到完全成熟的階段。但“非關係型資料庫”作為未來企業骨架的潛力仍不能忽視。特別是不遠的將來企業將更多地處理大容量的半結構化/非結構化以及最終一致性的資料,而不是相對而言小容量的,嚴格結構化的遵循ACID的資料。 所以現在而言至關重要的是做企業的關鍵決策人的思想工作,讓他們明白企業的資料處理需要使用“非關係型資料庫”。在這一過程中,要採取一些漸進的步驟把“非關係型資料庫”應用到企業IT的一些關鍵的方面(技術,人員和流程),併產生一定的價值。這樣,就可以用一種緩慢而穩健的方式從整體上來解決我們之前所總結出來的一系列問題。

採用一個產品/解決方案

如今市場上的選擇非常多樣化,可根據“非關係型資料庫”側重的面不同而進行差異化的處理。與此同時,企業應用場景可能需要不同型別的特點。然而以不同的解決方案來處理不同的應用/使用場景從規模經濟的角度出發對於企業是不適宜的。因此最好是根據目標應用的需要最終落實到某一個具體的產品/解決方案上。需要注意的是大多數的解決方案在特性上都會有一些折中,有些特性可能在其它的產品中可以獲得,有些可能只是在發展路線圖當中暫時設定了一個位置。因為大部分的產品會在不久的將來不斷趨於成熟,因此可以透過不同配置來提供不同的解決方案。所以只要現有的解決方案能適合目前大部分的需要,不妨作為一個起點將其採納。

選擇產品/解決方案的經驗法則

  • 支援所需要的邏輯資料模型應當被給予更高的權重。這將從實質上決定該解決方案在當前或未來能否靈活地適應不同的業務需求。
  • 調查該產品所支援的物理資料模型的合適與否,據此對這一解決方案所需要的水平伸縮性、可靠性、一致性和分割槽性作出合理的評估。這同樣能表明備份和恢復機制的可能性。
  • 介面支援需要與企業標準的執行環境對齊。由於這些產品支援多樣的介面,所以這一點可以得到很好的處理。
  • 只要產品支援水平伸縮性,對於持久化模型的選擇就不再重要了。

這裡有一份一系列“非關係型資料庫”的對照表。對於現在正認真考慮採用的企業來說,這是一個不錯的起點。為了更貼近企業本身的情況,從25+的集合中挑選出的子集所用到的的關鍵選擇標準是:

  1. 最重要的一點首先是企業應用程式必須支援有一定複雜程度的資料結構。否則的話,應用程式管理複雜性的責任將變得非常大。我認為比較合理的應當是介於純粹的鍵值對與關係型模式中間的一種方案。出於這方面的考慮像Vlodemort,Tokyo Cabinet等產品就排除在了我的列表之外。
  2. 第二點是以低成本的分片/分割槽為大容量資料提供水平支援。缺乏這樣的支援就使得解決方案與任何關係型資料庫無異了。因此像Neo4J(儘管他有豐富的基於圖的模型),Redis,CouchDB等此時此刻就被過濾出我的列表之外了。
  3. 最後一條評判標準,在企業級推廣之前我會考慮一定程度的商業支援。否則的話,一旦出現生產環境的問題,我該去找誰呢?出於這一點,我不得不將現在的一些明星產品排除在外,比如Cassandra(儘管有很大的可能不久的將來Rackspace或者Cloudera就會對其提供支援,因為它已經被用於一些生產環境裡邊了,比如Twitter,Digg,Facebook)。

有了這些過濾標準,我可以精簡這一列表,符合目前企業可用的產品有(下一版本就會提供shards支援), ,和。下面這個表格中總結了這四個產品的主要特性。一個企業可以基於自己具體的實際情況從中作出選擇,找到最適合自己需要的特性。

特性

MongoDB

Riak

HyperTable

HBase

邏輯資料模型

富文件,並提供對內嵌文件的支援

富文件

列家族(Column Family)

列家族(Column Family)

CAP支援

CA

AP

CA

CA

動態新增刪除節點

支援(很快在下一發布中就會加入)

支援

支援

支援

多DC支援

支援

不支援

支援

支援

介面

多種特定語言API(Java,Python,Perl,C#等)

HTTP之上的JSON

REST,Thrift,Java

C++,Thrift

持久化模型

磁碟

磁碟

記憶體加磁碟(可調的)

記憶體加磁碟(可調的)

相對效能

更優(C++編寫)

最優(Erlang編寫)

更優(C++編寫)

優(Java編寫)

商業支援

10gen.com

Basho Technologies

Hypertable Inc

Cloudera

資料訪問抽象

為資料訪問建立一個單獨的抽象層對於“非關係型資料庫”來說是必須的。它可以帶來多方面的好處。首先,應用開發者可以與底層解決方案的細節完全隔離開來。這對於技術方面的伸縮性帶來了好處。同時未來如果需要更改底層的解決方案也很方便。這也以一個標準的方式滿足了多個應用的要求(即去掉了Join,Group by等複雜特性的SQL)。

為效能和伸縮性建立模型

不管選擇怎樣的解決方案,使用標準技術(比如,分層排隊網路等)來對效能和伸縮性進行建模都是高度推薦的。它能夠為基本的伺服器規劃、拓撲以及整體的軟體許可證成本,管理執行等提供必要的資料。這將實質上成為所有預算計劃的主要參考資料,並對作出決策提供幫助。

構建顯式的冗餘

要防止資料丟失,除了將資料複製到備份伺服器上,沒有其它的辦法了。儘管許多非關係型資料庫提供自動複製功能,但仍然存在主節點單點失效的風險。因此最好是使用次節點備份,並準備好用於資料恢復和自動資料修復的指令碼。出於這樣的目的,應當充分的瞭解目標解決方案的物理資料模型,找出可能的恢復機制備選方案,基於企業的整體需求和實踐來對這些選項作出評估。

構建公共資料服務平臺

就像公共共享服務的關係型資料庫一樣,也可以構建非關係型資料庫的公共資料服務來促進規模經濟效應,滿足基礎設施和支援的需要。這對於未來進一步演化和更改也有幫助。這可以作為願望列表上的最終目標,透過中期或長期的努力來達到這一成熟水平。然而,初始階段就設立這樣的遠景有助於在整個過程中作出正確的決策。

壯大企業的技術力量

每個組織都有一部分人對於學習新生的和非傳統的事物充滿熱忱。成立這樣的小組,並挑選人員(全職的或兼職的),密切關注這方面的動向,瞭解問題和挑戰,進行前瞻性的思考,能夠為使用這些技術的專案提供方向和幫助。同時,這個小組還可以為決策者澄清炒作的疑雲,提供來自真實資料的觀點。

建立與產品社群的關係

選擇了產品之後,與產品社群建立起良好的關係對於雙方的成功都有極大的好處。許多非關係型資料庫目前都有十分活躍的社群,非常願意相互幫助。企業與社群之間的良好合作能給大家帶來一個雙贏的局面。如能提前對問題和解決方案有了解,那麼企業在對某些特性或版本作出決策時就能成竹在胸。反過來,企業又能對產品特性的路線圖產生影響作用,這對他們自身和社群都是有利的。另一方面,社群也能從實際層次的問題中得到反饋,從而豐富和完善產品。來自大型企業的成功案例同樣能讓他們處於領先。

迭代前進

考慮到非關係型資料庫相對的成熟度,風險最小的採用策略就是遵循迭代開發的方法論。構建公共資料服務平臺和標準化資料訪問抽象不可能是一蹴而就的。相反,透過迭代和麵向重構的方式能更好的達到目標。運用不太成熟的技術進行轉型的過程,中途改變解決方案也不會太意外的。與此同時,採用敏捷的方式來看待事物,能夠幫助建立起一個能從管理和實現兩方面不斷吸引改進的開放態度。

然而,在這一問題上實現迭代,非常重要的一點是定義一個決策條件矩陣。比如操作指南(和例子),來判斷一個應用的物件模型是否適合關係型或非關係的範圍,對基礎設施規劃作出指導,列出必需的測試用例等等。

結束語

企業的非關係型資料庫採用過程中最大的挑戰就是轉變決策者的思想觀念——讓他們相信並非所有的資料/物件都適合關係型資料庫。 最能證明這一點就是選擇合適的用例去嘗試非關係型資料庫,進而證實在合適的背景下,非關係型資料庫是比關係型資料庫更有效的解決方案。找到一些“非關鍵業務”(但能立竿見影的)適合於非關係型資料庫的專案。這些專案的成功(甚至失敗)都能有助於觀念的改變。這也能有助於不斷學習如何才能以一種不同的方式來更好的採用非關係型資料庫。這些少兒學步般的嘗試所作出的努力與投入都是值得的,如果企業想要在將來使用“非關係型資料庫”來重塑其資訊管理體系的話。

關於作者

Sourav Mazumder目前是InfoSys Technologies的首席技術架構師。他在資訊科技領域有14年以上的經驗。作為Infosys技術顧問團的主要成員,Sourav為Infosys在美國、歐洲、澳洲和日本的主要客戶,提供保險、電信、銀行、零售、安全、交通以及建築、工程、施工等多個行業的服務。他曾參與Web專案的技術架構和路線圖定義,SOA戰略實施,國際戰略定義,UI元件化,效能建模,伸縮性分析,非結構化資料管理等等。Sourav參考的Infosys自身的核心銀行產品Finacle,也為他提供了豐富的產品開發經驗。Sourav還曾參與開發Infosys的J2EE可重用框架,和定義Infosys在架構方面和開發定製應用方面的軟體工程方法。Sourav的經歷還包括在保證架構合規和開發專案的治理方面的工作。

Sourav是iCMG認證的軟體架構師,同時也是TOGAF 8認證的執行者。Sourav最近在LISA伯克利全球化會議上發表了演講。在社群裡十分流行。

Sourav目前關注NoSQL,Web2.0,治理,效能建構和全球化。

SQL vs NoSQL:資料庫併發寫入效能比拼

最近聽說了很多關於NoSQL的新聞,比如之前Sourceforge改用MongoDB,Digg改用Cassandra等等。再加上之前做資料庫比較時有人推薦我mongodb,所以也搜尋了一下NoSQL,覺得NoSQL可能真的是未來的趨勢。

NoSQL vs SQL

傳統SQL資料庫為了實現ACID(atomicity,consistency, isolation, durability),往往需要頻繁應用檔案鎖,這使得其在現代的web2.0應用中越來越捉襟見肘。現在SNS網站每一個點選都是一條/多條查詢,對資料庫寫的併發要求非常之高,而傳統資料庫無法很好地應對這種需求。而仔細想來SNS中大部分需求並不要求ACID,比如Like/Unlike投票等等。

NoSQL吸取了教訓,比如有些NoSQL採用了eventually consistency的概念,在沒有Update操作一段時間後,資料庫將最終是consistency的,顯然這樣的資料庫將能更好的支援高併發讀寫。

SQL資料庫是基於schema的,這對時時刻刻更新著的web2.0應用開發者來說是個噩夢:隨時隨地有新的應用出現,舊的資料庫無法適應新的應用,只能不停地更新schema,或者做補丁表,如此一來要麼schema越發混亂,要麼就是資料庫頻繁升級而耗時耗力耗錢。

NoSQL一般就沒有schema這種概念,大部分NoSQL都直接儲存json類的Row,比如一個記錄可以是{ id = 1, name = Bob, phone = 38492839 },這樣擴充套件升級非常方便,比如需要地址資訊直接加入 address=blahblah 即可。

傳統SQL很難進行分散式應用,即使可以也往往代價高昂。而NoSQL則很好地解決了這個問題:他們一般都直接從分散式系統中吸取了Map/Reduce方法,從而很容易就可以處理規模急速增加的問題。

推薦robbin牛的NoSQL資料庫探討之一 - 為什麼要用非關聯式資料庫?一文,介紹了主流的一些NoSQL系統,還有這個站收集了基本上目前所有的NoSQL系統。

總結一下我對NoSQL的看法,NoSQL出現的目的就是為了解決高併發讀寫的問題,而高併發應用往往需要分散式的資料庫來實現高效能和高可靠性,所以NoSQL的關鍵字就是concurrency和scalability。

我的瓶頸

我之前主要關注資料庫的select效能也就是read效能,在讀效能方面SQL資料庫並沒有明顯的劣勢,應該說純粹高併發讀的效能的話往往要優於NoSQL資料庫,然而一旦涉及寫,事情就不一樣了。

我本來以為自己不會遇到大量寫的問題,後來發現即使在simplecd這種簡單的應用環境下也會產生大量的併發寫:這就是爬VC使用者評論的時候。事實上,sqlite3在處理這個問題上非常的力不從心,所以我產生了換個資料庫的想法。

既然我是要求能高併發讀寫,乾脆就不用SQL了,但是同時我也想測試一下其他SQL的寫效能。

我的資料有180萬條,總共350M,測試用了10個執行緒,每個執行緒做若干次100個資料的bulk寫入,然後記錄總共耗時。結果如下:

innodb: 15.19
myiasm: 14.34
pgsql: 23.41
sqlite3: 鎖住了
sqlite3(單執行緒): 300+
mongodb: 3.82
couchdb: 90
couchdb(單執行緒):66

作為一個MySQL黑,看到這組測試資料我表示壓力很大。在SQL資料庫中,mysql意外地取得了最佳的成績,好於pgsql,遠好於sqlite。更令人意外的是myisam居然優於號稱insert比較快的innodb。不管如何,對我的應用來說,用mysql儲存評論資料是一個更為明智的選擇。我對mysql徹底改觀了,我宣佈我是mysql半黑。以後select-intensive的應用我還是會選擇sqlite,但是insert/update-intensive的應用我就會改用mysql了。

MongoDB和CouchDB同為NoSQL,表現卻截然相反,MongoDB效能很高,CouchDB的併發效能我只能ORZ,這種效能實在太抱歉了。

NoSQL的碎碎念

其實我本來還打算測試cassandra的,可是cassandra用的是java,這首先讓我眉頭一皺,記憶體大戶我養不起啊,其次看了cassandra的文件,立刻崩潰,這簡直就是沒有文件麼。(BTW,CouchDB也好不到哪裡去,我都是用python-couchdb然後help(couchdb.client)看用法的)

至於CouchDB,可能是因為採用http方式傳送請求,所以併發效能糟糕的一塌糊塗,很懷疑它是否有存在的理由。

MongoDB是我用下來最討人喜歡的一個NoSQL。不但文件豐富,使用簡單,效能也非常好,它的Map/Reduce查詢(很多NoSQL都有)讓我驚歎,資料庫可以非常簡單地就擴大規模,完全不用理會什麼分割槽分表之類繁瑣的問題,可惜這方面我暫時沒有需求。但是MongoDB有兩大致命問題。

第一是刪除鎖定問題,當批次刪除記錄時,資料庫還是會鎖定不讓讀寫。這意味著進行資料清理時會讓網站應用失去響應。見locking problems

第二是記憶體佔用問題,MongoDB用了作業系統的記憶體檔案對映,這導致作業系統會把所有空閒記憶體都分配給MongoDB,當MongoDB有這個需要時。更可怕的是,MongoDB從來不主動釋放已經霸佔的記憶體,它只會滾雪球一樣越滾越大,除非重啟資料庫。這樣的上下文環境下,MongoDB只適合一臺主機就一個資料庫,而沒有其他應用的環境,否則一會兒功夫MongoDB就會吃光記憶體,然後你都fork不出新程式,徹底悲劇。見memory limit

總之NoSQL雖然讓我眼前一亮,可是目前嘗試的一些產品都讓人望而生畏,現在的NoSQL都把目光放在了巨型網站上,而沒有一個小型的,可以在VPS裡面應用的高效能NoSQL,令我有點失望。NoSQL尚未成熟,很期待它的將來發展,目前來說MySQL還是更好的選擇。

 

谷歌Jeff Dean闡述分散式系統設計模式

分散式系統設計模式

1. 系統失敗是很平常的事情:每年有1-5%的硬碟會報廢,伺服器每年會平均當機兩次,報廢機率在2-4%機率。

2. 將一個大而複雜系統切分為多個服務:而且服務之間依賴儘可能的少,這樣有助於測試,部署和小團隊獨立開發。例子:一個google的搜尋會依賴100多個服務。吳注:需要一套機制來確保服務的fault-tolerant,不能讓一個服務的成敗影響全域性。

3. 需要有ProtocolDescription Language:比如protocol buffers。吳注:這樣能降低通訊方面的程式碼量。

4. 有能力在開發之前,根據系統的設計來預測效能:在最下面有一些重要的數字。

5.在設計系統方面,不要想做的很全面,而是需要抓住重點。

6. 為了增量做設計,但不為無限做設計。比如:要為5-50倍的增量做設計,但超過1000倍了,就需要重寫和重新設計了。

7. 使用備份請求來降低延遲:比如一個處理需要涉及1000臺機器,透過備份請求這個機制來避免這個處理被一臺慢機器延誤。吳注:這個機制非常適合MapReduce。

8. 使用範圍來分佈資料,而不是Hash:因為這樣在語義上比較簡單,並且容易控制。吳注:在大多數情況下語義比效能更重要,不要為了20%的情況hardcode。

9. 靈活的系統,根據需求來伸縮:並且當需求上來的時候,關閉部分特性,比如:關閉拼寫檢查。

10. 一個介面,多個實現。

11. 加入足夠的觀察和調式鉤子(hook)。

12. 1000臺伺服器只需單一Master:透過Master節點來統一指揮全部的行動,但客戶端和Master節點的互動很少,以免Master節點Crash,優點是,在語義上面非常清晰,但伸縮性不是非常強,一般最多隻能支援上千個節點。

13. 在一臺機器上執行多個單位的服務:當一臺機器當機時,能縮短相應的恢復時間,並且支援細粒度的負載均衡,比如在BigTable中,一個Tablet伺服器會執行多個Tablet。

ZooKeeper—分散式協同服務

1.概述

ZooKeeper[1]是hadoop的一個分散式協同服務,主要解決分散式應用程式中的區域性失敗問題,即網路操作過程中傳送者與接收者之間無法明確傳送操作是否正確無誤。在分散式系統中,它能夠提供:系統配置資訊維護,命名,分散式同步等服務。著名的Hadoop分散式資料庫HBase已經採用了ZooKeeper技術為其提供服務[2],如,ZK儲存了HBase中的Region的定址入口;實時監控Rgeion Server的狀態,將Region Server上、下線實時通知給master。

Zookeeper非常簡化暴漏一些簡單的基本操作,可以將其想象為一個簡單的檔案系統提供讀、寫操作服務,此外,它還有預定(ordering)和通知(notification)服務。ZK的可靠性體現在ZK服務是一一個叢集的方式提供服務,所以,分散式應用程式不會因為使用ZK伺服器而導致單點失敗問題,相反,很多分散式應用程式可以透過引入ZK來解決本身系統存在的單點問題,HBase就是這樣做的。

2.ZooKeeper安裝

        1.下載最新文件版本。

2.解壓:% tar xzf zookeeper-x.y.z.tar.gz 。配置環境變數:ZOOKEEPER_INSTALL指向ZK的安裝目錄,在PATH中加入可執行檔案目錄。簡單的辦法是在/etc/profile里加入下面兩行:

export ZOOKEEPER_INSTALL=/home/tom/zookeeper-x.y.z

export PATH=$PATH:$ZOOKEEPER_INSTALL/bin

ZK的配置檔案conf/zoo.cfg三個變數:

        trickTime= 2000   //ZK的基本時間單位,單位微秒

        dataDir=/usr/tom/zookeeper        //資料目錄,用於存放ZK的持久化資訊

        clientPort=2181                //ZK的預設監聽埠

        3.啟動ZK:

                           %zkServer.sh start

         4.確認安裝:

                           %echo ruok | nc localhost 2181

        如果返回資訊是imok(“Iam ok ”)的話表明安裝成功。

以上只是簡單的單機情況的配置。關於在實際生成系統中的安裝配置,後面將詳細介紹。

 

分散式資料庫的具體實現與對比分析

1.前言

隨著傳統的資料庫、計算機網路和數字通訊技術的快速發展,以資料分佈儲存和分佈處理為主要特徵的分散式資料庫系統的研究和開發越來越受到人們的關注。如何在一個資料庫系統中實現一個分散式資料庫,在實現分佈是資料庫中採用何種策略以及有那些需要注意的問題,這一直是資料庫研究和應用相關領域人員非常關心的問題。本文就在Microsoft SQL系列資料庫系統中分散式資料的具體實現進行了闡述,並對相關問題進行深入的分析。

 

2.分散式資料庫簡介

分散式資料庫系統是在集中式資料庫系統的基礎上發展起來的,由分散式資料庫管理系統和分散式資料庫組成,是資料庫技術與計算機網路技術的產物。分散式資料庫管理系統是具有管理分佈資料庫功能的計算機系統,分散式資料庫則是一組邏輯上屬同一系統,但物理上分佈在計算機網路的不同結點的結構化資料的集合,由分佈於計算機網路上的多個邏輯相關的資料庫組成。網路中的每個結點(場地)具有獨立處理的能力(稱為本地自治),可執行區域性應用,同時,每個結點透過網路通訊系統也能執行全域性應用。所謂區域性應用即僅對本結點的資料庫執行某些應用。所謂全域性應用(或分佈應用)是指對兩個以上結點的資料庫執行某些應用。支援全域性應用的系統才能稱為分散式資料庫系統。對使用者來說,一個分散式資料庫系統邏輯上看如同集中式資料庫系統一樣,使用者可在任何一個場地執行全域性應用。分散式資料庫系統的特點是:

(1)物理分佈性:分散式資料庫系統中的資料不是儲存在一個站點上,而是分散儲存在由計算機網路聯結起來的多個站點上。

(2)邏輯整體性:分散式資料庫系統中的資料物理上是分散在各個站點中,但這些分散的資料邏輯上卻是一個整體,它們被分散式資料庫系統的所有使用者(全域性使用者)共享,並由一個分散式資料庫管理系統統一管理。

(3)站點自治性:站點自治性也稱場地自治性,每個站點上的資料由本地的DBMS 管理,具有自治處理能力,完成本站點的區域性應用。

分散式資料庫系統適合於單位分散的部門,系統的結點可反映公司的邏輯組織,允許各部門將其常用資料存貯在本地,實施就地存放就地使用,降低通訊費用,並可提高響應速度。分散式資料庫可將資料分佈在多個結點上,增加適當的冗餘,可提高系統的可靠性,只要一個資料庫和網路可用,那麼全域性資料庫可一部分可用。不會因一個資料庫的故障而停止全部操作或引起效能瓶頸。故障恢復通常在單個結點上進行。結點可獨立地升級軟體。每個區域性資料庫存在一個資料字典。由於分散式資料庫系統結構的特點,它和集中式資料庫系統相比具有以下優點:

1) 可靠性高,個別場地發生故障,不致引起整個系統的癱瘓

2) 可擴充套件性,為擴充套件系統的處理能力提供了較好的途徑。

3) 均衡負載,可避免臨界瓶頸

目前,隨著計算機體系結構和資料庫技術的發展,分散式資料庫系統技術已經有了長足發展。基於關係資料模型的分散式資料庫技術在商業處理的應用方面已非常成功,如Oracle、MS SQL SERVER、Sybase、IBM DB2等資料庫平臺,其分散式處理技術能很好地滿足大型資料庫管理的需要,並能實現一定的分散式實時分析處理和資料更新,能夠滿足各種不同型別使用者對不同資料庫功能的要求。但不同的資料庫產品在價格、效能、穩定性等方面有著不小的差異,在對分散式資料庫理論的支援程度、分散式實現的具體方式也都有各自的特點,使用者應當根據各自的實際情況選用合適的資料庫產品。

 

3.分散式資料庫的具體實現

3.1實現的理論準備

3.1.1實現流程

由於以上集中式資料庫所不能替代的特點,分散式資料庫的使用已經越來越成為當前資料庫使用的主流。同時如何根據實際情況實現一個架構可靠、執行穩定的分散式資料庫也成為許多資料庫使用者所面臨的問題。綜合來講,設計並實現一個分散式資料庫主要有以下幾個步驟:

1) 掌握分散式資料庫的基本原理。

2) 根據需求和實際情況選取一種合適的分散式資料庫系統。

3) 瞭解在該資料庫系統中分散式資料庫的實現方式。

4) 在系統中的具體實現。

本文已經就以上幾點分別做了簡單闡述。下面將集中探討一下在MS SQL 2000資料庫系統中實現分散式資料庫的方式。

3.1.2 MS SQL 2000資料庫簡介

MS SQL 2000資料庫系統是微軟公司的主流資料庫產品,其工作效率、穩定性等指標都非常出色,且在具有友好、簡單的操作方式的同時,對分散式資料庫的實現有著完整的理論支援。且對不同型別的分散式資料庫應用需求都提出了完善的解決方案。所以對MS SQL 2000資料庫中分散式資料庫實現的掌握對分散式資料庫的實際設計以及在其它資料庫平臺上的實現都有很大的參考作

3.1.3"複製"技術與分散式事務處理

設計一個分散式計算解決方案首先需要考慮的問題就是應用的完整性、複雜性、效能和可用性以及響應時間等,同時還需要考慮的是對於不同的應用需求是採用實時存取遠端資料(分散式事務處理)還是採用延遲存取遠端資料("複製")。

MS SQL 2000資料庫中的分散式事務處理即透過事務處理機制採用實時資料存取,能夠保證資料的一致性。是一種實時遠端存取和實時更新資料的技術。這種技術可以保證應用的完整性並降低了應用的複雜性,但是如果系統存在網路存取速度很慢這樣的問題,相應響應時間就會很慢。這是一種同步分發資料庫技術。

"複製",顧名思義就是將資料庫中的資料複製到不同物理地點的資料庫中以支援分散式應用,它是整個分散式計算解決方案的一個重要組成部分。這裡針對"複製"也存在同步"複製"和非同步"複製"的問題。同步"複製",複製資料在任何時間在任何複製節點均保持一致。如果複製環境中的任何一個節點的複製資料發生了更新操作,這種變化會立刻反映到其他所有的複製節點。這種技術適用於那些對於實時性要求較高的商業應用中。非同步"複製",所有複製節點的資料在一定時間內是不同步的。如果複製環境中的其中的一個節點的複製資料發生了更新操作,這種改變將在不同的事務中被傳播和應用到其他所有複製節點。這些不同的事務間可以間隔幾秒,幾分種,幾小時,也可以是幾天之後。複製節點之間的資料是不同步的,但傳播最終將保證所有複製節點間的資料一致。這是一種延遲遠端存取和延遲傳播對資料更新的技術,有很高的可用性和很短的響應時間,相比同步分發資料庫技術就顯得複雜一些,為了確保應用的完整性需要仔細考慮和設計。

對於實際的商業問題,必須權衡這兩種技術的利弊最終選擇最佳的解決方案,有些問題選用分散式事務處理比較適合,也有一些問題採用"複製"是比較好的解決方案,還有一些問題必須綜合這兩種技術。

3.1.4 "發行-分佈-訂閱"結構

MS SQL 2000資料庫系統中分散式資料庫的具體實現採用"發行-分佈-訂閱"結構。具體來講就是將實現分散式資料的角色分為三種,即發行者、分佈者、訂閱者。這三中角色在資料的分佈中其著不同的作用:

1) 發行者:是發行專案的集合。發行專案也就是資料庫中表、儲存過程或特定的行或列等我們要作為分散式資料的這部分內容。

2) 訂閱者:從發行者上進行資料的訂閱,即按照需求使用發行者上的資料更新自己本地的資料。其訂閱方式包括推出式訂閱(屬於主動式釋出,發行者主動將資料傳送到訂閱者處)、引進式訂閱(屬於被動式釋出,即當訂閱者需要更新資料時再向發行者請求進行更新)

3) 釋出者:負責管理,將發行者上發行的資料安計劃傳送到訂閱者處。

透過採用"發行-分佈-訂閱"結構,分散式資料庫中每一個角色的功能更加明確。同時應該注意的是,三種角色指的是再分散式資料實現中所起到的作用,並不是指具體的計算機。同一臺計算機可能扮演多種角色,即一臺主機在分散式資料庫中有可能即使發行者,也是訂閱者,或者三種都是。

3.2 "複製"策略的選擇

MS SQL 2000資料庫採用的"複製"方式有快照複製(Snapshot Replication)、事務複製(transaction replication)和合並複製(merge replication)三種,三者都是按照實現制定的"複製"策略,在一定的時間,按照一定的規則進行一定數量釋出內容的複製,三者的具體差別及特點如下:

1) 快照複製:在複製時將所有釋出資料全部重新傳送一遍。此方法的特點是具有周期性、進行批次複製處理、高延遲、高度自治。但由於一次傳送資料較多,靈活性差,故不適宜大型資料庫。但同時此方法不需持續監控,故代價較低。

2) 事務複製:屬於增量複製,即在複製時僅複製增減或變更的內容,特點是低延遲、低度自治,適宜大型資料庫。但此方法需要持續監控,故代價較高。另外,此方法複製的方式也可設定為即時更新。

3) 合併複製:複製時即根據發行者上的資料,也根據訂閱者上來進行更新。此方法高度自治,在過程中使用使用觸發器,但由於其自身的實現方式,故不能保證資料一致性

不同的"複製"策略適用於不同的分散式資料庫實現要求,我們可以根據延遲、站點自治、事務一致性、資料更新衝突、"複製"發生的頻率需求和網路特性的各方面的實際情況來決定使用那一種或那幾種"複製"策略。

3.3 "複製"的實現

在決定好"複製"的策略後,要在系統中進行具體的實現。其主要步驟包括:

3.3.1調整、設定發行者和分佈者

透過對預釋出的資料的瞭解和對接受資料者的判斷等,我們已經確定了"複製"的策略,這一步我們就需要確定"複製"發生的頻率需求並根據網路特性,如拓撲結構、"複製"型別的影響、空間需求等對發行者的發行內容和分佈者的分佈策略進行具體的設定。

3.3.2設定訂閱者

透過分散式資料的要求和訂閱者特徵決定訂閱者的屬性,如選擇訂閱方式是"推出式訂閱"還是"引進式訂閱"等,此屬性為最重要的內容。然後進行其他訂閱服務屬性設定。

3.3.3 設定代理服務

分散式資料庫中的代理服務是進行"複製"管理的核心,它是一種始終執行的服務,負責全部"複製"任務的控制和管理。這些服務中主要包括:快照代理、分佈代理、日誌代理和合並代理等。每一種"複製"方法都需要一種或集中代理服務的配合。

透過以上幾部分內容的設定,我們就可以建立並執行起一個完整、可行的分散式資料庫。但經管這個資料庫能夠正確的實現並執行,並不一定意味者其"複製"機制能夠始終保持穩定並一直符合我們的要求,所以,我們還需要對"複製"服務進行有效的管理。這就包括使用伺服器的"複製"服務監視工具來對"複製"服務的維護,並在"複製"服務發生故障時進行的問題解決。

 

4.MS SQL 2000資料庫對異類資料庫"複製"的支援

MS SQL 2000資料庫可以與其他異類資料庫實現資料直接"複製",包括Microsoft Access資料庫、Oracle系列資料庫、IBM DB2資料庫以及其他支援 SQL Server ODBC介面標準的資料庫。這些異類資料庫可以透過"複製"代理直接連線起來,組成一個大的分散式資料庫,自由進行分散式資料的"複製"處理。

 

5.小結

分散式資料庫以其高度的可擴充套件性和可伸縮性,同時由於資源共享提高了系統的價效比,已經得到廣泛的研究和應用。本文就在MS SQL 2000資料庫中實現分散式資料庫的策略與具體方式做了詳細的分析。MS SQL 2000資料庫是一款常用的資料庫產品,其分散式資料功能針對各種實際情況都有著完善的解決方案。透過對其分散式資料庫實現策略、方法和特點的學習,可以加深我們對分散式資料庫理論的認識,加強我們透過具體途徑,解決實際問題的能力。

 

 

Hadoop:Google分散式儲存/計算/查詢系統的開源實現

Google的偉大很大程度上得益於其強大的資料儲存和計算能力,GFS和Bigtable使得其基本擺脫了昂貴的人力運維,並節省了機器資源;MapReduce使其可以很快看到各種搜尋策略試驗的效果。鑑於此,國內外出現了很多的模仿者,它們都是所謂的“高科技”企業,且往往還打上“雲端計算”的標籤。從頭到尾實現一套Google的儲存/計算/查詢系統是極其複雜的,也只有寥寥無幾的幾個巨頭可以做到,Hadoop做為一種開源的簡化實現,幫了很多科技公司的大忙。前些時候,Yahoo將Hadoop的創始人收於麾下,使得Hadoop完成華麗大轉身,效能實現了一個飛躍式提升。
  Hadoop主要包括HDFS(分散式檔案系統,對應GFS),MapReduce(分散式計算系統)和HBase(分散式查詢系統,對應Bigtable),其中以HDFS和MapReduce較為成熟。另外,Hadoop還包括一些輔助系統,如分散式鎖服務ZooKeeper,對應GoogleChubby。這一套系統的設計目標如下:
  1. 簡化運維:在大規模叢集中,機器當機,網路異常,磁碟錯都屬於正常現象,因此錯誤檢查,自動恢復是核心架構目標。Google的解決方案就已經做到了機器隨時加入/離開叢集。
  2. 高吞吐量:高吞吐量和低延遲是兩個矛盾的目標,Hadoop優先追求高吞吐量,設計和實現中採用了小操作合併,基於操作日誌的更新等提高吞吐量的技術。
  3. 節省機器成本:Hadoop鼓勵部署時利用大容量的廉價機器(價效比高但是機器故障機率大),資料的儲存和服務也分為HDFS和HBase兩個層次,從而最大限制地利用機器資源。
  4. 採用單Master的設計:單Master的設計極大地簡化了系統的設計和實現,由此帶來了機器規模限制和單點失效問題。對於機器規模問題,由於Hadoop是資料/計算密集型系統,而不是後設資料密集型系統,單Master設計的單個叢集可以支援成千上萬臺機器,對於現在的幾乎所有應用都不成問題;而單點失效問題可以透過分散式鎖服務或其它機制有效地解決。

  Google的其它模仿者包括,Microsoftdyrad(模範GoogleMapReduce),Hypertable(HadoopHBase開發團隊核心成員開始的一個開源專案,C++實現的Bigtable)。Google的解決方案不是萬能的,然而相對我們普通人已經是幾乎不可逾越了。Hadoop做為Google的這個模型的簡化實現,有很多不足,這裡先列出幾點,以後將透過閱讀Hadoop原始碼和論文逐漸展開分析。Hadoop的幾個明顯缺點如下:
  1. 採用Java實現。Java的IO處理雖然沒有效能瓶頸,但是對於CPU密集型的任務是一個噩耗。這點可以透過對比HBase和Hypertable兩個開源的Bigtable實現來做初步的驗證。
  2. 開源專案。開源本身是一柄雙刃劍,它方便了大多數人,但是對於一個有一定規模的公司,專案發展方向的把握,技術保密,技術支援等都是採用Hadoop這種開源專案必須考慮的問題。另外,Hadoop作為一個比較新的專案,效能和穩定性的提升還需要一定時間。

 

 

Moosefs介紹


MooseFS是一種分散式檔案系統,MooseFS檔案系統結構包括以下四種角色:
  1 管理伺服器managingserver (master)
  2 後設資料日誌伺服器Metaloggerserver(Metalogger)
  3 資料儲存伺服器data servers(chunkservers)
  4 客戶機掛載使用clientcomputers
各種角色作用:
   1 管理伺服器:負責各個資料儲存伺服器的管理,檔案讀寫排程,檔案空間回收以及恢復.多節點複製
  2 後設資料日誌伺服器: 負責備份master伺服器的變化日誌檔案,檔案型別為changelog_ml.*.mfs,以便於在master server出問題的時候接替其進行工作
  3 資料儲存伺服器:負責連線管理伺服器,聽從管理伺服器排程,提供儲存空間,併為客戶提供資料傳輸.
  4 客戶端: 透過fuse核心介面掛接遠端管理伺服器上所管理的資料儲存伺服器,.看起來共享的檔案系統和本地unix檔案系統使用一樣的效果.吧。

1 Moosefs簡介

1.1 角色構成

  整個mfs共計四種角色:master、metalogger、chunk和client
  1、master:只有一臺。
  2、metalogger:可以有多臺。它負責定期從master下載metadata,並實時同步changelog。metadata和changelog的關係類似於sfrd裡面基準和增量的關係。當master掛了的時候,metalogger利用下載下來的metadata和實時同步的changelog來恢復master掛掉時候的metadata。並且接管master的功能。
  3、chunk:提供儲存的伺服器,可以有多臺。這些伺服器負責提供儲存,它可以自由的啟動和停止。在chunk啟動後,會主動與master聯絡,master知道有多少chunk在網路中,並且會定期檢查chunk的狀態。
  4、client:使用mfs的伺服器,可以有多臺。它需要執行mfsmount命令,將網路上的儲存掛載到本地,看起來類似nfs。client就像讀寫本地磁碟那樣讀寫mfsmount掛載的網路儲存。

 

解讀:基於Hadoop的大規模資料處理系統

2010年8月27日下午,由中國電子學會雲端計算專家委員會主辦、CSDN承辦的“高階雲端計算課程”在中關村軟體園進行了免費的公開課程,公開課上座無虛席,原定幾十人的教室,最終擠滿了上百人。本次高階課程以“普及雲端計算,利用雲端計算,發展雲端計算”為基本原則,旨在為雲端計算領域培養和選拔更多更優秀的技術和管理人才奠定堅實的基礎。

 在本次公開課上,中科院計算所副研究員查禮博士做了主題演講,解密了基於Hadoop的大規模資料處理系統的組成及原理。

 

Hadoop的組成部分

 

Hadoop是Google的MapReduce一個Java實現。MapReduce是一種簡化的分散式程式設計模式,讓程式自動分佈到一個由普通機器組成的超大叢集上併發執行。

Hadoop主要由HDFS、MapReduce和HBase等組成。具體的組成如下圖:

 

 

Hadoop的組成圖

1. Hadoop HDFS是Google GFS儲存系統的開源實現,主要應用場景是作為平行計算環境(MapReduce)的基礎元件,同時也是BigTable(如HBase、HyperTable)的底層分散式檔案系統。HDFS採用master/slave架構。一個HDFS叢集是有由一個Namenode和一定數目的Datanode組成。Namenode是一箇中心伺服器,負責管理檔案系統的namespace和客戶端對檔案的訪問。Datanode在叢集中一般是一個節點一個,負責管理節點上它們附帶的儲存。在內部,一個檔案其實分成一個或多個block,這些block儲存在Datanode集合裡。Namenode執行檔案系統的namespace操作,例如開啟、關閉、重新命名檔案和目錄,同時決定block到具體Datanode節點的對映。Datanode在Namenode的指揮下進行block的建立、刪除和複製。Namenode和Datanode都是設計成可以跑在普通的廉價的執行Linux的機器上。HDFS採用Java語言開發,因此可以部署在很大範圍的機器上。一個典型的部署場景是一臺機器跑一個單獨的Namenode節點,叢集中的其他機器各跑一個Datanode例項。這個架構並不排除一臺機器上跑多個Datanode,不過這比較少見。

 

 

HDFS體系結構圖

2. Hadoop MapReduce是一個使用簡易的軟體框架,基於它寫出來的應用程式能夠執行在由上千個商用機器組成的大型叢集上,並以一種可靠容錯的方式並行處理上TB級別的資料集。

一個MapReduce作業(job)通常會把輸入的資料集切分為若干獨立的資料塊,由 Map任務(task)以完全並行的方式處理它們。框架會對Map的輸出先進行排序,然後把結果輸入給Reduce任務。通常作業的輸入和輸出都會被儲存在檔案系統中。整個框架負責任務的排程和監控,以及重新執行已經失敗的任務。

 

Hadoop MapReduce處理流程圖

 

3. Hive是基於Hadoop的一個資料倉儲工具,處理能力強而且成本低廉。

 

 主要特點

 

儲存方式是將結構化的資料檔案對映為一張資料庫表。

提供類SQL語言,實現完整的SQL查詢功能。

1.可以將SQL語句轉換為MapReduce任務執行,十分適合資料倉儲的統計分析。

 

不足之處:

 

1.採用行儲存的方式(SequenceFile)來儲存和讀取資料。

2.效率低:當要讀取資料表某一列資料時需要先取出所有資料然後再提取出某一列的資料,效率很低。

3.佔用較多的磁碟空間。

 

由於以上的不足,查禮博士介紹了一種將分散式資料處理系統中以記錄為單位的儲存結構變為以列為單位的儲存結構,進而減少磁碟訪問數量,提高查詢處理效能。這樣,由於相同屬性值具有相同資料型別和相近的資料特性,以屬性值為單位進行壓縮儲存的壓縮比更高,能節省更多的儲存空間。

 

 行列儲存的比較圖

HBase是一個分散式的、面向列的開源資料庫,它不同於一般的關聯式資料庫,是一個適合於非結構化資料儲存的資料庫。另一個不同的是HBase基於列的而不是基於行的模式。HBase使用和 BigTable非常相同的資料模型。使用者儲存資料行在一個表裡。一個資料行擁有一個可選擇的鍵和任意數量的列,一個或多個列組成一個ColumnFamily,一個Fmaily下的列位於一個HFile中,易於快取資料。表是疏鬆的儲存的,因此使用者可以給行定義各種不同的列。在HBase中資料按主鍵排序,同時表按主鍵劃分為多個HRegion。

 

HBase資料表結構圖

“高階雲端計算課程”培訓將於2010年9月10日正式開始,將會介紹典型雲端計算平臺核心演算法,並透過案例講解企業雲端計算發展與建設規劃。欲知詳情,請登入

 DRDA 分散式關聯式資料庫體系結構

DRDA(Distributed Relational Database Architecture )分散式關聯式資料庫體系結構 
分散式關聯式資料庫體系結構(DRDA)是一個跨IBM平臺訪問、遵循SQL標準的資料庫資訊的IBM標準。它是IBM的資訊倉庫框架中的重要組成部分,該框架定義了龐大的後臺伺服器,客戶機可透過較小的基於工作組的中介伺服器來訪問它。DRDA具有下列功能:   
定義了客戶機和後臺資料庫之間的介面協議。   
提供了IBM的DB2、DBM、SQL/DS和SQL/400資料庫系統的互連框架。   
支援多供應商提供的資料庫系統。   
支援分散式資料庫上的事務(工作單元)處理。   
在DRDA中,客戶機叫做應用請求器(ARS),後臺伺服器叫做應用伺服器(AS),協議叫做應用支援協議(ASP),提供AR和AS間的介面。整個過程操作在SNA網上,但也計劃支援OSI和TCP/IP。有一個附加的協議叫做資料庫支援協議(DSP),它使一個AS能對另一伺服器扮演AR的角色。透過這種方法伺服器之間能相互通話並傳遞來自客戶AR的請求,如圖D-25所示。最初的協議對一個資料庫只支援一個結構化查詢語言(SQL)的語句,但未來的版本將對一個或多個資料庫提供多個語句的支援。   
DRDA是IBM環境中建立客戶機/伺服器計算的基礎之一。其它基礎是高階的對等聯網(APPN)和分散式資料管理(DDM)。透過資訊倉庫和DRDA,IBM計算機將它的企業中心組成部分的大型計算機,用作各種型別資訊(包括多媒體資訊)的儲存平臺。  

 

 

Google檔案系統GFS

Google檔案系統(Google File System,GFS)是一個大型的分散式檔案系統。它為Google雲端計算提供海量儲存,並且與Chubby、MapReduce以及Bigtable等技術結合十分緊密,處於所有核心技術的底層。由於GFS並不是一個開源的系統,我們僅僅能從Google公佈的技術文件來獲得一點了解,而無法進行深入的研究。

當前主流分散式檔案系統有RedHat的GFS[3](Global File System)、IBM的GPFS[4]、Sun的Lustre[5]等。這些系統通常用於高效能運算或大型資料中心,對硬體設施條件要求較高。以Lustre檔案系統為例,它只對後設資料管理器MDS提供容錯解決方案,而對於具體的資料儲存節點OST來說,則依賴其自身來解決容錯的問題。例如,Lustre推薦OST節點採用RAID技術或SAN儲存區域網來容錯,但由於Lustre自身不能提供資料儲存的容錯,一旦OST發生故障就無法恢復,因此對OST的穩定性就提出了相當高的要求,從而大大增加了儲存的成本,而且成本會隨著規模的擴大線性增長。

正如李開復所說的那樣,創新固然重要,但有用的創新更重要。創新的價值,取決於一項創新在新穎、有用和可行性這三個方面的綜合表現。Google GFS的新穎之處並不在於它採用了多麼令人驚訝的技術,而在於它採用廉價的商用機器構建分散式檔案系統,同時將GFS的設計與Google應用的特點緊密結合,並簡化其實現,使之可行,最終達到創意新穎、有用、可行的完美組合。GFS使用廉價的商用機器構建分散式檔案系統,將容錯的任務交由檔案系統來完成,利用軟體的方法解決系統可靠性問題,這樣可以使得儲存的成本成倍下降。由於GFS中伺服器數目眾多,在GFS中伺服器當機是經常發生的事情,甚至都不應當將其視為異常現象,那麼如何在頻繁的故障中確保資料儲存的安全、保證提供不間斷的資料儲存服務是GFS最核心的問題。GFS的精彩在於它採用了多種方法,從多個角度,使用不同的容錯措施來確保整個系統的可靠性。

2.1.1 系統架構

GFS的系統架構如圖2-1[1]所示。GFS將整個系統的節點分為三類角色:Client(客戶端)、Master(主伺服器)和Chunk Server(資料塊伺服器)。Client是GFS提供給應用程式的訪問介面,它是一組專用介面,不遵守POSIX規範,以庫檔案的形式提供。應用程式直接呼叫這些庫函式,並與該庫連結在一起。Master是GFS的管理節點,在邏輯上只有一個,它儲存系統的後設資料,負責整個檔案系統的管理,是GFS檔案系統中的“大腦”。Chunk Server負責具體的儲存工作。資料以檔案的形式儲存在Chunk Server上,Chunk Server的個數可以有多個,它的數目直接決定了GFS的規模。GFS將檔案按照固定大小進行分塊,預設是64MB,每一塊稱為一個Chunk(資料塊),每個Chunk都有一個對應的索引號(Index)。

圖2-1 GFS體系結構

客戶端在訪問GFS時,首先訪問Master節點,獲取將要與之進行互動的Chunk Server資訊,然後直接訪問這些Chunk Server完成資料存取。GFS的這種設計方法實現了控制流和資料流的分離。Client與Master之間只有控制流,而無資料流,這樣就極大地降低了Master的負載,使之不成為系統效能的一個瓶頸。Client與Chunk Server之間直接傳輸資料流,同時由於檔案被分成多個Chunk進行分散式儲存,Client可以同時訪問多個Chunk Server,從而使得整個系統的I/O高度並行,系統整體效能得到提高。

相對於傳統的分散式檔案系統,GFS針對Google應用的特點從多個方面進行了簡化,從而在一定規模下達到成本、可靠性和效能的最佳平衡。具體來說,它具有以下幾個特點。

1.採用中心伺服器模式

GFS採用中心伺服器模式來管理整個檔案系統,可以大大簡化設計,從而降低實現難度。Master管理了分散式檔案系統中的所有後設資料。檔案劃分為Chunk進行儲存,對於Master來說,每個Chunk Server只是一個儲存空間。Client發起的所有操作都需要先透過Master才能執行。這樣做有許多好處,增加新的Chunk Server是一件十分容易的事情,Chunk Server只需要註冊到Master上即可,Chunk Server之間無任何關係。如果採用完全對等的、無中心的模式,那麼如何將Chunk Server的更新資訊通知到每一個Chunk Server,會是設計的一個難點,而這也將在一定程度上影響系統的擴充套件性。Master維護了一個統一的名稱空間,同時掌握整個系統內Chunk Server的情況,據此可以實現整個系統範圍內資料儲存的負載均衡。由於只有一箇中心伺服器,後設資料的一致性問題自然解決。當然,中心伺服器模式也帶來一些固有的缺點,比如極易成為整個系統的瓶頸等。GFS採用多種機制來避免Master成為系統效能和可靠性上的瓶頸,如儘量控制後設資料的規模、對Master進行遠端備份、控制資訊和資料分流等。

2.不快取資料

快取(Cache)機制是提升檔案系統效能的一個重要手段,通用檔案系統為了提高效能,一般需要實現複雜的快取機制。GFS檔案系統根據應用的特點,沒有實現快取,這是從必要性和可行性兩方面考慮的。從必要性上講,客戶端大部分是流式順序讀寫,並不存在大量的重複讀寫,快取這部分資料對系統整體效能的提高作用不大;而對於Chunk Server,由於GFS的資料在Chunk Server上以檔案的形式儲存,如果對某塊資料讀取頻繁,本地的檔案系統自然會將其快取。從可行性上講,如何維護快取與實際資料之間的一致性是一個極其複雜的問題,在GFS中各個Chunk Server的穩定性都無法確保,加之網路等多種不確定因素,一致性問題尤為複雜。此外由於讀取的資料量巨大,以當前的記憶體容量無法完全快取。對於儲存在Master中的後設資料,GFS採取了快取策略,GFS中Client發起的所有操作都需要先經過Master。Master需要對其後設資料進行頻繁操作,為了提高操作的效率,Master的後設資料都是直接儲存在記憶體中進行操作。同時採用相應的壓縮機制降低後設資料佔用空間的大小,提高記憶體的利用率。

3.在使用者態下實現

檔案系統作為作業系統的重要組成部分,其實現通常位於作業系統底層。以Linux為例,無論是本地檔案系統如Ext3檔案系統,還是分散式檔案系統如Lustre等,都是在核心態實現的。在核心態實現檔案系統,可以更好地和作業系統本身結合,向上提供相容的POSIX介面。然而,GFS卻選擇在使用者態下實現,主要基於以下考慮。

在使用者態下實現,直接利用作業系統提供的POSIX程式設計介面就可以存取資料,無需瞭解作業系統的內部實現機制和介面,從而降低了實現的難度,並提高了通用性。

POSIX介面提供的功能更為豐富,在實現過程中可以利用更多的特性,而不像核心程式設計那樣受限。

使用者態下有多種除錯工具,而在核心態中除錯相對比較困難。

使用者態下,Master和Chunk Server都以程式的方式執行,單個程式不會影響到整個作業系統,從而可以對其進行充分最佳化。在核心態下,如果不能很 好地掌握其特性,效率不但不會高,甚至還會影響到整個系統執行的穩定性。

使用者態下,GFS和作業系統執行在不同的空間,兩者耦合性降低,從而方便GFS自身和核心的單獨升級。

4.只提供專用介面

通常的分散式檔案系統一般都會提供一組與POSIX規範相容的介面。其優點是應用程式可以透過作業系統的統一介面來透明地訪問檔案系統,而不需要重新編譯程式。GFS在設計之初,是完全面向Google的應用的,採用了專用的檔案系統訪問介面。介面以庫檔案的形式提供,應用程式與庫檔案一起編譯,Google應用程式在程式碼中透過呼叫這些庫檔案的API,完成對GFS檔案系統的訪問。採用專用介面有以下好處。

降低了實現的難度。通常與POSIX相容的介面需要在作業系統核心一級實現,而GFS是在應用層實現的。

採用專用介面可以根據應用的特點對應用提供一些特殊支援,如支援多個檔案併發追加的介面等。

專用介面直接和Client、Master、Chunk Server互動,減少了作業系統之間上下文的切換,降低了複雜度,提高了效率。

2.1.2 容錯機制

1.Master容錯

具體來說,Master上儲存了GFS檔案系統的三種後設資料。

名稱空間(Name Space),也就是整個檔案系統的目錄結構。

Chunk與檔名的對映表。

Chunk副本的位置資訊,每一個Chunk預設有三個副本。

首先就單個Master來說,對於前兩種後設資料,GFS透過操作日誌來提供容錯功能。第三種後設資料資訊則直接儲存在各個Chunk Server上,當Master啟動或Chunk Server向Master註冊時自動生成。因此當Master發生故障時,在磁碟資料儲存完好的情況下,可以迅速恢復以上後設資料。為了防止Master徹底當機的情況,GFS還提供了Master遠端的實時備份,這樣在當前的GFS Master出現故障無法工作的時候,另外一臺GFS Master可以迅速接替其工作。

2.Chunk Server容錯

GFS採用副本的方式實現Chunk Server的容錯。每一個Chunk有多個儲存副本(預設為三個),分佈儲存在不同的Chunk Server上。副本的分佈策略需要考慮多種因素,如網路的拓撲、機架的分佈、磁碟的利用率等。對於每一個Chunk,必須將所有的副本全部寫入成功,才視為成功寫入。在其後的過程中,如果相關的副本出現丟失或不可恢復等狀況,Master會自動將該副本複製到其他Chunk Server,從而確保副本保持一定的個數。儘管一份資料需要儲存三份,好像磁碟空間的利用率不高,但綜合比較多種因素,加之磁碟的成本不斷下降,採用副本無疑是最簡單、最可靠、最有效,而且實現的難度也最小的一種方法。

GFS中的每一個檔案被劃分成多個Chunk,Chunk的預設大小是64MB,這是因為Google應用中處理的檔案都比較大,以64MB為單位進行劃分,是一個較為合理的選擇。Chunk Server儲存的是Chunk的副本,副本以檔案的形式進行儲存。每一個Chunk以Block為單位進行劃分,大小為64KB,每一個Block對應一個32bit的校驗和。當讀取一個Chunk副本時,Chunk Server會將讀取的資料和校驗和進行比較,如果不匹配,就會返回錯誤,從而使Client選擇其他Chunk Server上的副本。

2.1.3 系統管理技術

嚴格意義上來說,GFS是一個分散式檔案系統,包含從硬體到軟體的整套解決方案。除了上面提到的GFS的一些關鍵技術外,還有相應的系統管理技術來支援整個GFS的應用,這些技術可能並不一定為GFS所獨有。

1.大規模叢集安裝技術

安裝GFS的叢集中通常有非常多的節點,文獻[1]中最大的叢集超過1000個節點,而現在的Google資料中心動輒有萬臺以上的機器在執行。因此迅速地安裝、部署一個GFS的系統,以及迅速地進行節點的系統升級等,都需要相應的技術支撐。

2.故障檢測技術

GFS是構建在不可靠的廉價計算機之上的檔案系統,由於節點數目眾多,故障發生十分頻繁,如何在最短的時間內發現並確定發生故障的Chunk Server,需要相關的叢集監控技術。

3.節點動態加入技術

當有新的Chunk Server加入時,如果需要事先安裝好系統,那麼系統擴充套件將是一件十分煩瑣的事情。如果能夠做到只需將裸機加入,就會自動獲取系統並安裝執行,那麼將會大大減少GFS維護的工作量。

4.節能技術

有關資料表明,伺服器的耗電成本大於當初的購買成本,因此Google採用了多種機制來降低伺服器的能耗,例如對伺服器主機板進行修改,採用蓄電池代替昂貴的UPS(不間斷電源系統),提高能量的利用率。Rich Miller 在一篇關於資料中心的部落格文章中表示,這個設計讓 Google 的 UPS 利用率達到99.9%,而一般資料中心只能達到92%~95%。

分散式系統儲存設計

曾幾何時,分散式計算成為一種潮流,伴之而來的分散儲存所帶來的高額管理費用已成為企業的一大筆開支,且給企業的業務發展帶來許多負面影響。儲存整合解決方案正是針對這類情況所設計。
現狀及問題
隨著計算機應用的不斷深入,目前企業的業務系統採用計算機來實現和儲存關鍵資料,已是非常普遍的現象。在過去的幾年,有這麼一種觀點:企業採用計算機系統和儲存系統最好採用“分散式計算”的方式。這種“分散式計算”的核心是:企業中每個部門選擇各自不同的電腦系統。因為每個部門各有各的業務,對不同品牌和平臺也各有偏好,選擇各自熟悉的平臺,以部門為單位分開,可以方便管理,同時節約成本。隨著這幾年的實踐,人們發現這一想法走向了它初衷的反面,具體體現在:
1. 資料格式的不統一所導致的管理困難,不利於業務的發展。多年以來,各地方各自為政,按照自己的喜好採購硬體,開發軟體系統,導致不同的系統平臺,資料格式也不完全統一,資料之間的遷移/轉換更加複雜,需要額外的硬體和軟體支援;而且,最為關鍵的是總公司沒有一本完整的賬目,不能做出及時的決策,對企業的發展和競爭極為不利;
2. 分散式管理所帶的鉅額管理費用已經得不償失。以前,企業往往會陷入一個誤區,認為分散式管理會節省費用,但一旦購買了一定量的儲存容量後,管理費用(維護費用,升級費用,人工費用等)成為最大的支出專案,而且這筆支出將貫穿整個產品的生命週期,因此管理費用是整套產品的最大投資,而且這個管理費用還在不斷上升。右圖比較了三種不同儲存分佈的情況下,花相同的管理費用實際可管理到的儲存容量。第一種情況為一個跨地域的企業將儲存分散在不同的地域分開管理;第二種情況為一個跨地域的企業將儲存集中在一個地方,但依然分為不同系統管理。第三種情況為完全儲存整合方案,即同一系統同一地域的管理。如圖所示,第三種方式可極大的降低成本同時方便管理。
 
圖:同樣的開支在不同環境下所能管理的容量
因此,銀行,電信等均在著手對儲存資料進行整合。
 
今天來說,集中化的儲存管理思想是一種非常有效的經濟的儲存管理解決方案。因為對於磁碟陣列來說,只有一套管理系統,這樣就可以極為方便地進行磁碟監控,效能除錯。增加或者重新配置磁碟也變得非常簡單。最大化的合成集中裝置,也使得儲存系統的當機風險降至最低。同時,一套完善的備份方案就可以有效地進行資料備份及恢復。
惠普提供兩種儲存整合解決方案。
1. 單一儲存裝置整合:
 

將資料庫和OA的資料集中存放在同一儲存裝置上。該儲存裝置支援開放標準,可連結不同平臺的伺服器(hp,sun,IBM、NTServer等),如hp的SureStore系列儲存裝置中的XP512、XP48等。下圖為整合後的邏輯示意圖:
2. 儲存區域網(SAN)整合
儲存整合的終極目標是構成儲存區域網,即由儲存區域網完成資料的讀、寫、備份和容災,無需佔用企業內部的通訊網路(LAN-Free)。前端的使用者無需考慮資料的存放位置,資料保護機制,備份策略等,僅僅象用自來水那樣擰開水龍頭。具體請參照儲存網路方案介紹。
 

下圖為整合後的儲存區域網的示意圖:
對比兩種儲存解決方案,兩者均採用惠普的同一儲存裝置,因而能夠
1. 有效利用總儲存空間,節約投資;假設某臺主機裝置的儲存空間不足,均可動態地進行調整,將其他主機暫時未佔用的空間進行分配,從而避免重複投資。
2. 能夠集中備份和維護,輕鬆管理;裝置集中在某一中心,一方面,只需少量的專業管理人員,維護起來也更加容易,另一方面,總公司也便於提起資料來支援決策支援系統。
3. 只需與一家廠商溝通,便於得到最好的服務。和以前方式不同,並非每家主機系統配置自己的儲存裝置,因而,一旦出現問題,需要和多家廠商進行溝通,甚至還會出現踢皮球現象,現在儲存裝置能夠支援多平臺,不會存在此類現象。
但前一種解決方案,需要透過區域網實現某些儲存和備份功能,存在網路瓶頸,而採用SAN整合方式,則解決了網路瓶頸問題,最大限度地提高整套系統的效能。
方案的優點
儲存整合一直是惠普公司關注的焦點,同時惠普公司在使用者儲存系統整合的方案設計和實施方面積累了豐富的經驗和大量的成功案例。透過建立惠普儲存整合計算環境後:
1. 可以極大地保護使用者的投資,降低使用者管理費用,從而
確保整體擁有成本最低。
2. 降低管理難度,維護資料管理的統一性。
3. 提高了電子化資料管理的可靠性。資料的集中化管理,
能夠確保資料的一致性和完整性,保證電子化資料的可
靠性。
4. 開放的標準,能夠支援多平臺的整合,包括:hp UX,
IBM AIX, Sun Solaris和Microsoft,Windows NT, W2K.  
方案配置
單一儲存裝置整合配置:
伺服器:
Option1: hp 9000 Unix伺服器;
hp NetServer PC伺服器;
Option2: IBM AIX或COMPAQ ALPHA,
SUN Solaris其他NT伺服器
儲存裝置:
hp SureStore XP48或XP512磁碟陣列
hp SureStore Tape Library 6/140,10/180,20/700
軟體:
hp SureSoft軟體
Option1: hp OmnibackII備份軟體及選件
Option2: Veritas NetBackup(hp UX, Solaris, NT)
備份軟體及選件
Option3: Veritas Backup Exec(NT)備份軟體及選件
Option4:CA ARCserve備份軟體及選件
Option5: Legato Networker備份軟體及選件
hp SAN儲存裝置整合配置:
伺服器:
Option1: hp 9000 Unix伺服器;
hp NetServer PC伺服器;
Option2: IBM AIX 或COMPAQALPHA,  
SUN Solaris其他NT伺服器
儲存裝置:
hp SureStore XP48或XP512磁碟陣列
hp SureStore Tape Library 6/140,10/180,20/700
其他硬體裝置:
hp SureStore SCSI Bridge FC4/2
Brocade Silkworm Switch 2400/2800
Emulex LP8000 主機匯流排適配卡(NT/WIN2K/AIX)
Qlogic QLA2200F主機匯流排適配卡(NT/WIN2K)
JNI FCE6410-N主機匯流排適配卡(NT/WIN2K)
JNI FC641063主機匯流排適配卡(Solaris, Sbus匯流排)
JNI FCI-1063 主機匯流排適配卡(Solaris,PCI匯流排)
光纖通道或廣域網介面
軟體:
hp SureSoft軟體
Option1: hp OmnibackII備份軟體及選件
Option2: Veritas NetBackup(hp UX, Solaris, NT)
備份軟體及選件
Option3: Veritas Backup Exec(NT)備份軟體及選件
Option4: CA ARCserve備份軟體及選件
Option5: Legato Networker備份軟體及選件
適用範圍
惠普的儲存整合方案適用於企業計算環境中有不同的儲存平臺,或儲存裝置分散在不同場所,希望透過儲存整合降低成本,同時提高可管理性的場合。

 

可維護性分散式儲存系統和分離的分散式系統

1.分散式系統的應該有兩種基本的層次的架構。

       1.1.普通的分散式系統架構,是典型的三層的架構,如下圖的分離的分散式系統的一個子系統。

       1.2.多個分散式系統構成的分散式系統的超級,可以構建雲服務的分散式系統。

 

2.普通的分散式系統的構成

2.1簡單分散式的組合構成的服務系統

  一般的分散式系統都具有三層架構層次,hand,master,svr。master儲存路由表,hand為接入,svr為真正的邏輯伺服器。可以對master的路由表操作實現分佈。master動態探測svr的存活,進行路由表的更新。

  如果單純的邏輯伺服器(不帶cache),這裡的路由表的更新較為方便和簡潔。如果是帶有cache 的邏輯伺服器則需要根據是髒cache還是乾淨cache,並遷移cache 的資料。由於在遷移過程中一般會對服務有影響,如果是髒cache,則需要等到遷移完成才能進行服務,這裡就有個格子鎖定的狀態。因此對於具有髒cache 的服務分散式系統,複雜程度大了很多。並且在遷移的時候會影響到服務。

 

 

 

 

 

                                             圖一  各個分散式的簡單組合構成的服務系統典型架構

 

       上圖中有些系統沒有hand模組是因為其master可能只需要管理其路由表。而路由表的拉取直接放在上一層的邏輯伺服器。其接入機是映象的接入。這種邏輯服務系統一般只能較小規模的業務接入和應用。原有有以下幾點:

       2.1.1.各個自己系統的具有自己的簡單容災機制,但是沒有總體的容災機制,

       2.1.2.所有的接入是映象的,則其配置都是一樣的,如果這個系統承載多個服務,則無法輕鬆對其近區別服務,遷移和管理。即一個系統一旦搭建也執行,則很難對業務應用的服務進行控制,控制的粒度則一個系統級別。

       2.1.3.其承載的業務愈多,則風險係數陡增。其接入伺服器的bug會 導致所有服務的中斷,其後端的邏輯伺服器的bug會導致其一個系統的癱瘓。其業務的灰度升級無法做到真正意義上的灰度。因為其子系統沒有業務管理的功能。無論灰度那一臺機器,其影響都是系統的所有業務,一旦出現bug,將會對所有的業務產生影響。無法做到業務和機器的灰度,只能做到機器這個低階別的灰度。

 

       因此這種分散式的簡單組合無法承載較大業務的執行,只有將一定數量的業務放在一個系統,這樣風險或許可承受,但是如果系統有所發展,接入的業務和應用越來越多,將會維護這樣若干的系統。造成運維上的麻煩和低效率。

 

3.可維護的分散式系統

  3.1 下面介紹可維護的分散式系統的構成和基本設計思想。目標就是要構建支援大規模業務應用的儲存分散式儲存系統。

    既然是儲存系統,那麼就會容納許多業務,這個系統才有意義。當前的能夠容納很多業務的成熟概念就是雲的概念,沒錯,我麼就是要構建這樣一個具有云系統概念的分散式儲存系統。

    其基本目標就是要能夠輕鬆的做到對不同業務的管理和維護,排程。在大規模的業務的場景下,能夠很好的支撐和運營。

   下面是其基本的架構思想。

 

 3.2基本設計思想:

       3.2.1 整個系統由統一的接入,統一的控制中心組成,後端的邏輯分散式子系統組成

       3.2.2 整個系統的master都具有容災,路由和按照一箇中心master的指令來進行操作的功能,即中心master具有管理的功能,能夠對業務進行控制和管理,然後根據策略中心生成的策略對整個系統進行均衡,遷移和管理操作。

       3.2.3 支援大規模的業務接入,提高系統的安全性。如果某一個業務有故障,中心master可以根據一定的策略將其佈置在單獨的機器行,並且將其他業務部署在其他機器上,實現業務的快速隔離。

       3.2.4 其對業務級別和機器級別,模組級別的灰度將表現非常優秀。中心master可以部署某個業務的某些訪問在灰度的機器上。灰度的控制非常靈活和到位。

 

4.總結

 

      具有可維護的分散式系統需要一個統一的控制中心。即cmaster(中心控制器)。可以對業務進行很好的排程和控制,所有的master

都應該具有這樣的功能,具有排程和控制的功能。這樣的系統才具有云服務系統的基礎

 

集中式,分散式,協作式資料處理的區別

1)集中式資料處理
集中式計算機網路由一個大型的中央系統,其終端是客戶機,資料全部儲存在中央系統,由資料庫管理系統進行管理,所有的處理都由該大型系統完成,終端只是用來輸入和輸出。終端自己不作任何處理,所有任務都在主機上進行處理。
    集中式資料儲存的主要特點是能把所有資料儲存在一個地方,各地辦公室的遠端終端透過電纜同中央計算機(主機)相聯,保證了每個終端使用的都是同一資訊。備份資料容易,因為他們都儲存在伺服器上,而伺服器是唯一需要備份的系統。這還意味這伺服器是唯一需要安全保護的系統,終端沒有任何資料。銀行的自動提款機(ATM)採用的就是集中式計算機網路。另外所有的事務都在主機上進行處理,終端也不需要軟碟機,所以網路感染病毒的可能性很低。這種型別的網路總費用比較低,因為主機擁有大量儲存空間、功能強大的系統,而使終端可以使用功能簡單而便宜的微機和其他終端裝置。
這類網路不利的一面是來自所有終端的計算都由主機完成,這類網路處理速度可能有些慢。另外,如果使用者有各種不同的需要,在集中式計算機網路上滿足這些需要可能是十分困難的,因為每個使用者的應用程式和資源都必須單獨設定,而讓這些應用程式和資源都在同一臺集中式計算機上操作,使得系統效率不高。還有,因為所有使用者都必須連線到一臺中央計算機,集中連線可能成為集中式網路的一個大問題。由於這些限制,如今的大多數網路都採用了分散式和協作式網路計算模型。
2)分散式資料處理
由於個人計算機的效能得到極大的提高及其使用的普及,使處理能力分佈到網路上的所有計算機成為可能。分散式計算是和集中式計算相對立的概念,分散式計算的資料可以分佈在很大區域。
分散式網路中,資料的儲存和處理都是在本地工作站上進行的。資料輸出可以列印,也可儲存在軟盤上。透過網路主要是得到更快、更便捷的資料訪問。因為每臺計算機都能夠儲存和處理資料,所以不要求伺服器功能十分強大,其價格也就不必過於昂貴。這種型別的網路可以適應使用者的各種需要,同時允許他們共享網路的資料、資源和服務。在分散式網路中使用的計算機既能夠作為獨立的系統使用,也可以把它們連線在一起得到更強的網路功能。
分散式計算的優點是可以快速訪問、多使用者使用。每臺計算機可以訪問系統內其他計算機的資訊檔案;系統設計上具有更大的靈活性,既可為獨立的計算機的地區使用者的特殊需求服務,也可為聯網的企業需求服務,實現系統內不同計算機之間的通訊;每臺計算機都可以擁有和保持所需要的最大資料和檔案;減少了資料傳輸的成本和風險。為分散地區和中心辦公室雙方提供更迅速的資訊通訊和處理方式,為每個分散的資料庫提供作用域,資料儲存於許多儲存單元中,但任何使用者都可以進行全域性訪問,使故障的不利影響最小化,以較低的成本來滿足企業的特定要求。
分散式計算的缺點是:對病毒比較敏感,任何使用者都可能引入被病毒感染的檔案,並將病毒擴散到整個網路。備份困難,如果使用者將資料儲存在各自的系統上,而不是將他們儲存在中央系統中,難於制定一項有效的備份計劃。這種情況還可能導致使用者使用同一檔案的不同版本。為了執行程式要求效能更好的PC機;要求使用適當的程式;不同計算機的檔案資料需要複製;對某些PC機要求有足夠的儲存容量,形成不必要的儲存成本;管理和維護比較複雜;裝置必須要互相相容。
3)協作式資料處理
協作式資料處理系統內的計算機能夠聯合處理資料,處理既可集中實施,也可分割槽實施。協作式計算允許各個客戶計算機合作處理一項共同的任務,採用這種方法,任務完成的速度要快於僅在一個客戶計算機執行。協作式計算允許計算機在整個網路內共享處理能力,可以使用其它計算機上的處理能力完成任務。除了具有在多個計算機系統上處理任務的能力,該型別的網路在共享資源方面類似於分散式計算。
協作式計算和分散式計算具有相似的優缺點。例如協作式網路上可以容納各種不同的客戶,協作式計算的優點是處理能力強,允許多使用者使用。缺點是病毒可迅速擴散到整個網路。因為資料能夠在整個網路記憶體儲,形成多個副本,檔案同步困難。並且也使得備份所有的重要資料比較困難。

 

 

Google Megastore分散式儲存技術全揭秘

 

Megastore是谷歌一個內部的儲存系統,它的底層資料儲存依賴Bigtable,也就是基於NoSql實現的,但是和傳統的NoSql不同的 是,它實現了類似RDBMS的資料模型(便捷性),同時提供資料的強一致性解決方案(同一個datacenter,基於MVCC的事務實現),並且將資料 進行細顆粒度的分割槽(這裡的分割槽是指在同一個datacenter,所有datacenter都有相同的分割槽資料),然後將資料更新在機房間進行同步複製 (這個保證所有datacenter中的資料一致)。

Megastore的資料複製是透過paxos進行同步複製的,也就是如果更新一個資料,所有機房都會進行同步更新,因為使用paxos進行復制, 所以不同機房針對同一條資料的更新複製到所有機房的更新順序都是一致的,同步複製保證資料的實時可見性,採用paxos演算法則保證了所有機房更新的一致 性,所以個人認為megastore的更新可能會比較慢,而所有讀都是實時讀(對於不同機房是一致的),因為部署有多個機房,並且資料總是最新。

為了達到高可用性,megastore實現了一個同步的,容錯的,適合長距離連線的日誌同步器

為了達到高可擴充套件性,megastore將資料分割槽成一個個小的資料庫,每一個資料庫都有它們自己的日誌,這些日誌儲存在NoSql中

Megastore將資料分割槽為一個Entity Groups的集合,這裡的Entity Groups相當於一個按id切分的分庫,這個Entity Groups裡面有多個Entity Group(相當於分庫裡面的表),而一個Entity Group有多個Entity(相當於表中的記錄)

在同一個Entity Group中(相當於單庫)的多個Entity的更新事務採用single-phase ACID事務,而跨Entity Group(相當於跨庫)的Entity更新事務採用two-phase ACID事務(2段提交),但更多使用Megastore提供的高效非同步訊息實現。需要說明的一點是,這些事務都是在同一個機房的,機房之間的資料互動都 是透過資料複製來實現的。

傳統關係型資料庫使用join來滿足使用者的需求,對於Megastore來說,這種模型(也就是完全依賴join的模型)是不合適的。原因包括

1.高負載互動性型應用能夠從可預期的效能提升得到的好處多於使用一種代價高昂的查詢語言所帶來的好處。

2.Megastore目標應用是讀遠遠多於寫的,所以更好的方案是將讀操作所需要做的工作轉移到寫操作上面(比如透過具體值代替外來鍵以消除join)

3.因為megastore底層儲存是採用BigTable,而類似BigTable的key-value儲存對於存取級聯資料是直接的

所以基於以上幾個原因,Megastore設計了一種資料模型和模式語言來提供基於物理地點的細顆粒度控制,級聯佈局,以及申明式的不正規資料儲存來幫助消除大部分joins。查詢時只要指定特定表和索引即可。

當然可能有時候不得不使用到join,Megastore提供了一種合併連線演算法實現,具體演算法這裡我還是沒弄清楚,原文是[the user providesmultiple queries that return primary keys for the same table in the same order;we then return the intersection of keys for all the provided queries.]

使用Megastore的應用透過並行查詢實現了outer joins。通常先進行一個初始的查詢,然後利用這個查詢結果進行並行索引查詢,這個過程我理解的是,初始查詢查出一條資料,就馬上根據這個結果進行並行 查詢,這個時候初始查詢繼續取出下一條資料,再根據這個結果並行查詢(可能前面那個外來鍵查詢還在繼續,使用不同的執行緒)。這種方法在初始查詢資料量較小並 且外來鍵查詢使用並行方式的情況下,是一種有效的並且具有sql風格的joins。

Megastore的資料結構介於傳統的RDBMS和NoSql之間的,前者主要體現在他的schema表示上,而後者體現在具體的資料儲存上 (BigTable)。和RDBMS一樣,Megastore的資料模型是定義schema中並且是強型別的。每一個schema有一個表集合,每個表包 含一個實體集合(相當於record),每個實體有一系列的屬性(相當於列屬性),屬性是命名的,並且指定型別,這些型別包括字串,各種數字型別,或者google的protocol buffer。這些屬性可以被設定成必需的,可選的,或者可重複的(一個屬性上可以具有多個值)。一個或者多個屬性可以組成一個主鍵。

在上圖中,User和Photo共享了一個公共屬性user_id,IN TABLE User這個標記直接將Photo和User這兩張表組織到了同一個BigTable中,並且鍵的順序(PRIMARY KEY(user_id,photo_id)?是這個還是schema中定義的順序?)保證Photo的實體儲存在對應的User實體鄰接位置上。這個機 制可以遞迴的應用,加速任意深度的join查詢速度。這樣,使用者能夠透過操作鍵的順序強行改變資料級聯的佈局。其他標籤請參考原文。

Megastore支援事務和併發控制。一個事務寫操作會首先寫入對應Entity Group的日誌中,然後才會更新具體資料。BigTable具有一項在相同row/column中儲存多個版本帶有不同時間戳的資料。正是因為有這個特 性,Megastore實現了多版本併發控制(MVCC,這個包括oracle,innodb都是使用這種方式實現ACID,當然具體方式會有所不同): 當一個事務的多個更新實施時,寫入的值會帶有這個事務的時間戳。讀操作會使用最後一個完全生效事務的時間戳以避免看到不完整的資料.讀寫操作不相互阻塞, 並且讀操作在寫事務進行中會被隔離(?)。

Megastore 提供了current,snapshot,和inconsistent讀,current和snapshot級別通常是讀取單個entity group。當開始一個current讀操作時,事務系統會首先確認所有之前提交的寫已經生效了;然後系統從最後一個成功提交的事務時間戳位置讀取資料。 對於snapshot讀取,系統拿到己經知道的完整提交的事務時間戳並且從那個位置直接讀取資料,和current讀取不同的是,這個時候可能提交的事務 更新資料還沒有完全生效(提交和生效是不同的)。Megastore提供的第三種讀就是inconsistent讀,這種讀無視日誌狀態並且直接讀取最後 一個值。這種方式的讀對於那些對減少延遲有強烈需求,並且能夠容忍資料過期或者不完整的讀操作是非常有用的。

一個寫事務通常開始於一個current讀操作以便確定下一個可用的日誌位置。提交操作將資料變更聚集到日誌,並且分配一個比之前任何一個都高的時 間戳,並且使用Paxos將這個log entry加入到日誌中。這個協議使用了樂觀併發:即使有可能有多個寫操作同時試圖寫同一個日誌位置,但只會有1個成功。所有失敗的寫都會觀察到成功的寫 操作,然後中止,並且重試它們的操作。諮詢式的鎖定能夠減少爭用所帶來的影響。透過特定的前端伺服器分批寫入似乎能夠完全避免競爭(這幾句有些不能理解) [ Advisory lockingis available to reduce the effects of contention. Batching writes throughsession affinity to a particular front-end server can avoid contentionaltogether.]。

完整事務生命週期包括以下步驟:

1.讀:獲取時間戳和最後一個提交事務的日誌位置

2.應用邏輯:從BigTable讀取並且聚集寫操作到一個日誌Entry

3.提交:使用Paxos將日誌Entry加到日誌中

4.生效:將資料更新到BigTable的實體和索引中

5.清理:刪除不再需要的資料

寫操作能夠在提交之後的任何點返回,但是最好還是等到最近的副本(replica)生效(再返回)。

Megastore提供的訊息佇列提供了在不同Entity Group之間的事務訊息。它們能被用作跨Entity Group的操作,在一個事務中分批執行多個更新,或者延緩工作(?)。一個在單個Entity Group上的事務能夠原子性地傳送或者收到多個資訊除了更新它自己的實體。每個訊息都有一個傳送和接收的Entity Group;如果這兩個Entity Group是不同的,那麼傳輸將會是非同步的。

訊息佇列提供了一種將會影響到多個Entity Group的操作的途徑,舉個例子,日曆應用中,每一個日曆有一個獨立的Entity Group,並且我們現在需要傳送一個邀請到多個其他人的日曆中,一個事務能夠原子地傳送邀請訊息到多個獨立日曆中。每個日曆收到訊息都會把邀請加入到它 自己的事務中,並且這個事務會更新被邀請人狀態然後刪除這個訊息。Megastore大規模使用了這種模式:宣告一個佇列後會自動在每一個Entity Group上建立一個收件箱。

Megastore支援使用二段提交進行跨Entity Group的原子更新操作。因為這些事務有比較高的延遲並且增加了競爭的風險,一般不鼓勵使用。

接下來內容具體來介紹下Megastore最核心的同步複製模式:一個低延遲的Paxos實現。Megastore的複製系統向外提供了一個單一 的,一致的資料檢視,讀和寫能夠從任何副本(repli ca)開始,並且無論從哪個副本的客戶端開始,都能保證ACID語義。每個Entity Group複製結束標誌是將這個Entity Group事務日誌同步地複製到一組副本中。寫操作通常需要一個資料中心內部的網路互動,並且會跑檢查健康狀況的讀操作。current級別的讀操作會有 以下保證:

1.一個讀總是能夠看到最後一個被確認的寫。(可見性)

2.在一個寫被確認後,所有將來的讀都能夠觀察到這個寫的結果。(永續性,一個寫可能在確認之前就被觀察到)

資料庫典型使用Paxos一般是用來做事務日誌的複製,日誌中每個位置都由一個Paxos例項來負責。新的值將會被寫入到之前最後一個被選中的位置之後。

Megastore在事先Paxos過程中,首先設定了一個需求,就是current reads可能在任何副本中進行,並且不需要任何副本之間的RPC互動。因為寫操作一般會在所有副本上成功,所以允許在任何地方進行本地讀取是現實的。這 些本地讀取能夠很好地被利用,所有區域的低延遲,細顆粒度的讀取failover,還有簡單的程式設計體驗。

Megastore設計實現了一個叫做Coordinator(協調者)的服務,這個服務分佈在每個副本的資料中心裡面。一個 Coordinator伺服器跟蹤一個Entity Groups集合,這個集合中的Entity Groups需要具備的條件就是它們的副本已經觀察到了所有的Paxos寫。在這個集合中的Entity Groups,它們的副本能夠進行本地讀取(local read)。

寫操作演算法有責任保持Coordinator狀態是保守的,如果一個寫在一個副本上失敗了,那麼這次操作就不能認為是提交的,直到這個entity group的key從這個副本的coordinator中去除。(這裡不明白)

為了達到快速的單次互動的寫操作,Megastore採用了一種Master-Slave方式的最佳化,如果一次寫成功了,那麼會順帶下一次寫的保證 (也就是下一次寫就不需要prepare去申請一個log position),下一次寫的時候,跳過prepare過程,直接進入accept階段。Megastore沒有使用專用的Masters,但是使用 Leaders。

Megastore為每一個日誌位置執行一個Paxos演算法例項。[ The leader for each log position is a

distinguished replicachosen alongside the preceding log position's consensus value.] Leader仲裁在0號提議中使用哪一個值。第一個寫入者向Leader提交一個值會贏得一個向所有副本請求接收這個值做為0號提議最終值的機會。所有其 他寫入者必需退回到Paxos的第二階段。

因為一個寫入在提交值到其他副本之前必需和Leader互動,所以必需儘量減少寫入者和Leader之間的延遲。Megastore設計了它們自己 的選取下一個寫入Leader的規則,以同一地區多數應用提交的寫操作來決定。這個產生了一個簡單但是有效的原則:使用最近的副本。(這裡我理解的是哪個 位置提交的寫多,那麼使用離這個位置最近的副本做為Leader)

Megastore的副本中除了有日誌有Entity資料和索引資料的副本外,還有兩種角色,其中一種叫做觀察者(Witnesses),它們只寫 日誌,並且不會讓日誌生效,也沒有資料,但是當副本不足以組成一個quorum的時候,它們就可以加入進來。另外一種叫只讀副本(Read-Only), 它剛剛和觀察者相反,它們只有資料的映象,在這些副本上只能讀取到最近過去某一個時間點的一致性資料。如果讀操作能夠容忍這些過期資料,只讀副本能夠在廣 闊的地理空間上進行資料傳輸並且不會加劇寫的延遲。

上圖顯示了Megastore的關鍵元件,包括兩個完整的副本和一個觀察者。應用連線到客戶端庫,這個庫實現了Paxos和其他一些演算法:選擇一個副本進行讀,延遲副本的追趕,等等。

Each applicationserver has a designated local replica. The client library makes Paxosoperations on that replica durable by submitting transactions directly to thelocal Bigtable.To minimize wide-area roundtrips, the library submits remotePaxos operations to stateless intermediary replication servers communicatingwith their local Bigtables.

客戶端,網路,或者BigTable失敗可能讓一個寫操作停止在一箇中間狀態。複製的伺服器會定期掃描未完成的寫入並且透過Paxos提議沒有操作的值來讓寫入完成。

接下來介紹下Megastore的資料結構和演算法,每一個副本存有更新和日誌Entries的後設資料。為了保證一個副本能夠參與到一個寫入的投票中 即使是它正從一個之前的當機中恢復資料,Megastore允許這個副本接收不符合順序的提議。Megastore將日誌以獨立的Cells儲存在 BigTable中。

當日志的字首不完整時(這個字首可能就是一個日誌是否真正寫入的標記,分為2段,第一段是在寫入日誌之前先寫入的幾個位元組,然後寫入日誌,第二段是 在寫入日誌之後寫入的幾個位元組,只有這個日誌字首是完整的,這個日誌才是有效的),日誌將會留下holes。下圖表示了一個單獨Megastore Entity Group的日誌副本典型場景。0-99的日誌位置已經被清除了,100的日誌位置是部分被清除,因為每個副本都會被通知到其他副本已經不需要這個日誌 了。101日誌位置被所有的副本接受了(accepted),102日誌位置被Y所獲得,103日誌位置被A和C副本接受,B副本留下了一個 hole,104日誌位置因為副本A和B的不一致,複本C的沒有響應而沒有一致結果。

在一個current讀的準備階段(寫之前也一樣),必需有一個副本要是最新的:所有之前更新必需提交到那個副本的日誌並且在該副本上生效。我們叫這個過程為catchup。

省略一些截止超時的管理,一個current讀演算法步驟如下:

1.本地查詢:查詢本地副本的Coordinator,判定當前副本的Entity Group是最新的

2.查詢位置:確定最高的可能已提交的日誌位置,然後選擇一個己經將這個日誌位置生效的副本

a.(Local read) 如果步驟1發現本地副本是最新的,那麼從本地副本中讀取最高的被接受(accepted)的日誌位置和時間戳。

b.(Majority read)如果本地副本不是最新的(或者步驟1或步驟2a超時),那麼從一個多數派副本中發現最大的日誌位置,然後選取一個讀取。我們選取一個最可靠的或者最新的副本,不一定總是是本地副本

3.追趕:當一個副本選中之後,按照下面的步驟追趕到已知的日誌位置:

a.對於被選中的不知道共識值的副本中的每一個日誌位置,從另外一個副本中讀取值。對於任何一個沒有已知已提交的值的日誌位置,發起一個沒有操作的寫操作。Paxos將會驅動多數副本在一個值上打成共識-----可能是none-op的寫操作或者是之前提議的寫操作

b.順序地將所有沒有生效的日誌位置生效成共識的值,並將副本的狀態變為到分散式共識狀態(應該是Coordinator的狀態更新)

如果失敗,在另外一個副本上重試。

4.驗證:如果本地副本被選中並且之前沒有最新,傳送一個驗證訊息到coordinator斷定(entitygroup,replica)能夠反饋(reflects)所有提交的寫操作。不要等待回應----如果請求失敗,下一個讀操作會重試。

5.查詢資料:從選中的副本中使用日誌位置所有的時間戳讀取資料。如果選中的副本不可用,選取另外一個副本重新開始執行追趕,然後從它那裡讀取。一個大的讀取結果有可能從多個副本中透明地讀取並且組裝返回

注意在實際使用中 1和2a通常是並行執行的。

在完整的讀操作演算法執行後,Megastore發現了下一個沒有使用的日誌位置,最後一個寫 操作的時間戳,還有下一個leader副本。在提交時刻,所有更新的狀態都變為打包的(packaged)和提議(proposed),並且包含一個時間 戳和下一個leader 候選人,做為下一個日誌位置的共識值。如果這個值贏得了分散式共識,那麼這個值將會在所有完整的副本中生效。否則整個事務將會終止並且必需重新從讀階段開 始。

就像上面所描述的,Coordinators跟蹤Entity Groups在它們的副本中是否最新。如果一個寫操作沒有被一個副本接受,我們必需將這個Entity Group的鍵從這個副本的Coordinator中移除。這個步驟叫做invalidation(失效)。在一個寫操作被認為提交的並且準備生效,所有 副本必需已經接受或者讓這個Entity Group在它們coordinator上失效。

寫演算法的步驟如下:

1.接受Leader:請求Leader接受值做為0號提議的值。如果成功。跳到第三步

2.準備:在所有副本上執行Paxos Prepare階段,使用一個關於當前log位置更高的提議號。將值替換成擁有最高提議號的那個值。[Replace the valuebeing written withthe highest-numbered proposal discovered, if any]

3.接受:請求餘下的副本接受這個值。如果多數副本失敗,轉到第二步。

4.失效:將沒有接受值的副本coordinator失效掉。錯誤處理將在接下來描述

5.生效:將更新在儘可能多的副本上生效。如果選擇的值不同於原始提議的,返回衝突錯誤[?]

Coordinator程式在每一個資料中心執行並且只保持其本地副本的狀態。在上述的寫入演算法中,每一個完整的副本必需接受或者讓其 coordinator失效,所以這個可能會出現任何單個副本失效就會引起不可用。在實際使用中這個不是一個尋常的問題。Coordinator是一個簡 單的程式,沒有其他額外的依賴並且沒有持久儲存,所以它表現得比一個BigTable伺服器更高的穩定性。然而,網路和主機失敗仍然能夠讓 coordinator不可用。

Megastore使用了Chubby鎖服務:Coordinators在啟動的時候從遠端資料中心獲取指定的Chubby locks。為了處理請求,一個Coordinator必需持有其多數locks。一旦因為當機或者網路問題導致它丟失了大部分鎖,它就會恢復到一個預設 保守狀態----認為所有在它所能看見的Entity Groups都是失效的。隨後(該Coordinator對應的)副本中的讀操作必需從多數其他副本中得到日誌位置直到Coordinator重新獲取到 鎖並且Coordinator的Entries重新驗證的。

寫入者透過測試一個Coordinator是否丟失了它的鎖從而讓其在Coordinator不可用過程中得到保護:在這個場景中,一個寫入者知道在恢復之前Coordinator會認為自己是失效的。

在一個資料中心活著的Coordinator突然不可用時,這個演算法需要面對一個短暫(幾十秒)的寫停頓風險---所有的寫入者必需等待 Coordinator的Chubby locks過期(相當於等待一個master failover後重新啟動),不同於master failover,寫入和讀取都能夠在coordinator狀態重建前繼續平滑進行。

除了可用性問題,對於Coordinator的讀寫協議必需滿足一系列的競爭條件。失效的資訊總是安全的,但是生效的資訊必需小心處理。在 coordinator中較早的寫操作生效和較晚的寫操作失效之間的競爭透過帶有日誌位置而被保護起來。標有較高位置的失效操作總是勝過標有較低位置的生 效操作。一個在位置n的失效操作和一個在位置m<n的生效操作之間的競爭常常和一個crash聯絡在一起。Megastore透過一個具有時間期限 的數字代表Coordinator來偵測crashes:生效操作只允許在最近一次對Coordinator進行的讀取操作以來時間期限數字沒變化的情況 下修改Coordinator的狀態。

總體來說,使用Coordinator從而能夠在任何資料中心進行快速的本地讀取對於可用性的影響並不是完全沒有的。但是實際上,以下因素能夠減輕使用Coordinator所帶來的問題。

1.Coordinators是比任何的BigTable 伺服器更加簡單程式,機會沒有依賴,所以可用性更高。

2.Coordinators簡單,均勻的工作負載讓它們能夠低成本地進行預防措施。

3.Coordinators輕量的網路傳輸允許使用高可用連線進行服務質量監控。

4.管理員能夠在維護期或者非安全期集中地讓一批Coordinators失效。對於默寫訊號的監測是自動的。

5.一個Chubby qunrum能夠監測到大多數網路問題和節點不可用。

總結

文章總體介紹了下google megastore的實現思路,其主要解決的問題就是如何在複雜的環境下(網路問題,節點失效等等)保證資料存取服務的可用性。對於多機房,多節點,以及ACID事務支援,實時非實時讀取,錯誤處理等等關鍵問題上給出了具體方案。

Oceanbase——千億級海量資料庫

從大學的資料結構課程可以知道,資料量比較大時,有兩種資料結構很常用:雜湊表和B+樹,分散式系統也是類似的。如下圖:

Amazon的系統實現了一個分散式雜湊表,而Google Bigtable, Yahoo PNUTS,Microsoft SQL Azure實現了一顆分散式B+樹。分散式雜湊表實現相對簡單,但只支援隨機讀取;而分散式B+樹支援範圍查詢,但實現比較複雜,主要有兩個難點:

1, 狀態資料的持久化和遷移。更新操作改變系統的狀態,資料庫系統中更新操作首先以事務提交日誌(MySQL稱為binlog, NOSQL稱為commit log)寫入到磁碟,為了保證可靠性,commit log需要複製多份並保證它們之間的一致性。另外,機器當機時需要透過commit log記錄的狀態修改資訊將服務遷移到叢集中的其它節點。

2, 子表的分裂和合並。B+樹實現的難點在於樹節點的分裂與合併,在分散式系統中,資料被順序劃分為大小在幾十到幾百MB大小的資料範圍,一般稱為子表,相當於B+樹結構中的葉子節點。由於每個子表在系統中儲存多份,需要保證多個副本之間的分裂點是一致的。由於子表在分裂的同時也有更新操作,保證多個副本之間一致是比較困難的。

對於這兩個問題,不同的系統有不同的解決方法:

1. 狀態維持。

Google Bigtable將狀態資料寫入到GFS中,由GFS提供可靠性保證,但GFS本身是一個巨大的工程;Yahoo PNUTS將狀態資料寫入到分散式訊息中介軟體,Yahoo內部稱為Yahoo Message Broker;Microsoft SQL Azure直接透過網路將資料複製到多機,由於一臺機器服務多個子表,這些子表的副本可能分佈在整個叢集中,因此,任何兩臺機器都可能建立資料複製的網路通道,需要處理與這些通道有關的異常情況。

2.子表分裂。

由於底層有GFS保證可靠性,Google Bigtable設計時保證每一個子表同時只被一臺機器(Tablet Server)服務;Yahoo PNUTS透過引入複雜的兩節點提交(Two-phase commit)協議協調多個副本之間的一致性,使得他們的分裂點相同;Microsoft SQL Azure乾脆不支援子表分裂,犧牲一部分擴充套件性從而簡化系統設計。

淘寶Oceanbase設計之初對淘寶的線上儲存需求進行分析發現:淘寶的資料總量比較大,未來一段時間,比如五年之內的資料規模為百TB級別,千億條記錄,另外,資料膨脹很快,傳統的分庫分表對業務造成很大的壓力,必須設計自動化的分散式系統;然而,線上儲存每天的修改量很小,大多數情況下單機的記憶體就能存放下。因此,我們採用將動態資料和靜態資料分離的辦法。動態資料的資料量小,採用集中式的方法解決,這樣,狀態資料維持從一個分散式的問題轉化為單機的問題;靜態資料的資料量大,採用分散式的方法解決,因為靜態資料基本不變,實現時不需要複雜的執行緒同步機制,另外,保證靜態資料的多個副本之間一致性是比較容易的,簡化了子表的分裂和合並操作。透過這樣的權衡,淘寶Oceanbase以一種很簡單的方式滿足了未來一段時間的線上儲存需求,並且還獲得了一些其它特性,如高效支援跨行跨表事務,這對於淘寶的業務是非常重要的。另外,我們之所以敢於做這樣的權衡,還有一個重要的原因:我們內部已經思考了很多關於動態資料由集中式變為分散式的方案,即使我們對需求估計有些偏差,也可以很快修改原有系統進一步提高可擴充套件性

分散式計算開源框架Hadoop入門實踐

Hadoop是Apache開源組織的一個分散式計算開源框架,在很多大型網站上都已經得到了應用,如亞馬遜、Facebook和Yahoo等等。

在SIP專案設計的過程中,對於它龐大的日誌在開始時就考慮使用任務分解的多執行緒處理模式來分析統計,在我從前寫的文章《Tiger ConcurrentPractice --日誌分析並行分解設計與實現》中有所提到。但是由於統計的內容暫時還是十分簡單,所以就採用Memcache作為計數器,結合MySQL就完成了訪問控制以及統計的工作。然而未來,對於海量日誌分析的工作,還是需要有所準備。現在最火的技術詞彙莫過於“雲端計算”,在Open API日益盛行的今天,網際網路應用的資料將會越來越有價值,如何去分析這些資料,挖掘其內在價值,就需要分散式計算來支撐海量資料的分析工作。

回過頭來看,早先那種多執行緒,多工分解的日誌分析設計,其實是分散式計算的一個單機版縮略,如何將這種單機的工作進行分拆,變成協同工作的叢集,其實就是分散式計算框架設計所涉及的。在去年參加BEA大會的時候,BEA和VMWare合作採用虛擬機器來構建叢集,無非就是希望使得計算機硬體能夠類似於應用程式中資源池的資源,使用者無需關心資源的分配情況,從而最大化了硬體資源的使用價值。分散式計算也是如此,具體的計算任務交由哪一臺機器執行,執行後由誰來彙總,這都由分散式框架的Master來抉擇,而使用者只需簡單地將待分析內容提供給分散式計算系統作為輸入,就可以得到分散式計算後的結果。

Hadoop是Apache開源組織的一個分散式計算開源框架,在很多大型網站上都已經得到了應用,如亞馬遜、Facebook和Yahoo等等。對於我來說,最近的一個使用點就是服務整合平臺的日誌分析。服務整合平臺的日誌量將會很大,而這也正好符合了分散式計算的適用場景(日誌分析和索引建立就是兩大應用場景)。

當前沒有正式確定使用,所以也是自己業餘摸索,後續所寫的相關內容,都是一個新手的學習過程,難免會有一些錯誤,只是希望記錄下來可以分享給更多志同道合的朋友。

什麼是Hadoop?

搞什麼東西之前,第一步是要知道What(是什麼),然後是Why(為什麼),最後才是How(怎麼做)。但很多開發的朋友在做了多年專案以後,都習慣是先How,然後What,最後才是Why,這樣只會讓自己變得浮躁,同時往往會將技術誤用於不適合的場景。

Hadoop框架中最核心的設計就是:MapReduce和HDFS。MapReduce的思想是由Google的一篇論文所提及而被廣為流傳的,簡單的一句話解釋MapReduce就是“任務的分解與結果的彙總”。HDFS是Hadoop分散式檔案系統(Hadoop Distributed File System)的縮寫,為分散式計算儲存提供了底層支援。

MapReduce從它名字上來看就大致可以看出個緣由,兩個動詞Map和Reduce,“Map(展開)”就是將一個任務分解成為多個任務,“Reduce”就是將分解後多工處理的結果彙總起來,得出最後的分析結果。這不是什麼新思想,其實在前面提到的多執行緒,多工的設計就可以找到這種思想的影子。不論是現實社會,還是在程式設計中,一項工作往往可以被拆分成為多個任務,任務之間的關係可以分為兩種:一種是不相關的任務,可以並行執行;另一種是任務之間有相互的依賴,先後順序不能夠顛倒,這類任務是無法並行處理的。回到大學時期,教授上課時讓大家去分析關鍵路徑,無非就是找最省時的任務分解執行方式。在分散式系統中,機器叢集就可以看作硬體資源池,將並行的任務拆分,然後交由每一個空閒機器資源去處理,能夠極大地提高計算效率,同時這種資源無關性,對於計算叢集的擴充套件無疑提供了最好的設計保證。(其實我一直認為Hadoop的卡通圖示不應該是一個小象,應該是螞蟻,分散式計算就好比螞蟻吃大象,廉價的機器群可以匹敵任何高效能的計算機,縱向擴充套件的曲線始終敵不過橫向擴充套件的斜線)。任務分解處理以後,那就需要將處理以後的結果再彙總起來,這就是Reduce要做的工作。

MapReduce結構示意圖

上圖就是MapReduce大致的結構圖,在Map前還可能會對輸入的資料有Split(分割)的過程,保證任務並行效率,在Map之後還會有Shuffle(混合)的過程,對於提高Reduce的效率以及減小資料傳輸的壓力有很大的幫助。後面會具體提及這些部分的細節。

HDFS是分散式計算的儲存基石,Hadoop的分散式檔案系統和其他分散式檔案系統有很多類似的特質。分散式檔案系統基本的幾個特點:

對於整個叢集有單一的名稱空間。

資料一致性。適合一次寫入多次讀取的模型,客戶端在檔案沒有被成功建立之前無法看到檔案存在。

檔案會被分割成多個檔案塊,每個檔案塊被分配儲存到資料節點上,而且根據配置會由複製檔案塊來保證資料的安全性。

上圖中展現了整個HDFS三個重要角色:NameNode、DataNode和Client。NameNode可以看作是分散式檔案系統中的管理者,主要負責管理檔案系統的名稱空間、叢集配置資訊和儲存塊的複製等。NameNode會將檔案系統的Meta-data儲存在記憶體中,這些資訊主要包括了檔案資訊、每一個檔案對應的檔案塊的資訊和每一個檔案塊在DataNode的資訊等。DataNode是檔案儲存的基本單元,它將Block儲存在本地檔案系統中,儲存了Block的Meta-data,同時週期性地將所有存在的Block資訊傳送給NameNode。Client就是需要獲取分散式檔案系統檔案的應用程式。這裡透過三個操作來說明他們之間的互動關係。

檔案寫入:

Client向NameNode發起檔案寫入的請求。

NameNode根據檔案大小和檔案塊配置情況,返回給Client它所管理部分DataNode的資訊。

Client將檔案劃分為多個Block,根據DataNode的地址資訊,按順序寫入到每一個DataNode塊中。

檔案讀取:

Client向NameNode發起檔案讀取的請求。

NameNode返回檔案儲存的DataNode的資訊。

Client讀取檔案資訊。

檔案Block複製:

NameNode發現部分檔案的Block不符合最小複製數或者部分DataNode失效。

通知DataNode相互複製Block。

DataNode開始直接相互複製。

最後再說一下HDFS的幾個設計特點(對於框架設計值得借鑑):

Block的放置:預設不配置。一個Block會有三份備份,一份放在NameNode指定的DataNode,另一份放在與指定DataNode非同一Rack上的DataNode,最後一份放在與指定DataNode同一Rack上的DataNode上。備份無非就是為了資料安全,考慮同一Rack的失敗情況以及不同Rack之間資料複製效能問題就採用這種配置方式。

心跳檢測DataNode的健康狀況,如果發現問題就採取資料備份的方式來保證資料的安全性。

資料複製(場景為DataNode失敗、需要平衡DataNode的儲存利用率和需要平衡DataNode資料互動壓力等情況):這裡先說一下,使用HDFS的balancer命令,可以配置一個Threshold來平衡每一個DataNode磁碟利用率。例如設定了Threshold為10%,那麼執行balancer命令的時候,首先統計所有DataNode的磁碟利用率的均值,然後判斷如果某一個DataNode的磁碟利用率超過這個均值Threshold以上,那麼將會把這個DataNode的block轉移到磁碟利用率低的DataNode,這對於新節點的加入來說十分有用。

資料交驗:採用CRC32作資料交驗。在檔案Block寫入的時候除了寫入資料還會寫入交驗資訊,在讀取的時候需要交驗後再讀入。

NameNode是單點:如果失敗的話,任務處理資訊將會紀錄在本地檔案系統和遠端的檔案系統中。

資料管道性的寫入:當客戶端要寫入檔案到DataNode上,首先客戶端讀取一個Block然後寫到第一個DataNode上,然後由第一個DataNode傳遞到備份的DataNode上,一直到所有需要寫入這個Block的NataNode都成功寫入,客戶端才會繼續開始寫下一個Block。

安全模式:在分散式檔案系統啟動的時候,開始的時候會有安全模式,當分散式檔案系統處於安全模式的情況下,檔案系統中的內容不允許修改也不允許刪除,直到安全模式結束。安全模式主要是為了系統啟動的時候檢查各個DataNode上資料塊的有效性,同時根據策略必要的複製或者刪除部分資料塊。執行期透過命令也可以進入安全模式。在實踐過程中,系統啟動的時候去修改和刪除檔案也會有安全模式不允許修改的出錯提示,只需要等待一會兒即可。

下面綜合MapReduce和HDFS來看Hadoop的結構:

在Hadoop的系統中,會有一臺Master,主要負責NameNode的工作以及JobTracker的工作。JobTracker的主要職責就是啟動、跟蹤和排程各個Slave的任務執行。還會有多臺Slave,每一臺Slave通常具有DataNode的功能並負責TaskTracker的工作。TaskTracker根據應用要求來結合本地資料執行Map任務以及Reduce任務。

說到這裡,就要提到分散式計算最重要的一個設計點:Moving Computation is Cheaper than Moving Data。就是在分散式處理中,移動資料的代價總是高於轉移計算的代價。簡單來說就是分而治之的工作,需要將資料也分而儲存,本地任務處理本地資料然後歸總,這樣才會保證分散式計算的高效性。

為什麼要選擇Hadoop?

說完了What,簡單地說一下Why。官方網站已經給了很多的說明,這裡就大致說一下其優點及使用的場景(沒有不好的工具,只用不適用的工具,因此選擇好場景才能夠真正發揮分散式計算的作用):

可擴充套件:不論是儲存的可擴充套件還是計算的可擴充套件都是Hadoop的設計根本。

經濟:框架可以執行在任何普通的PC上。

可靠:分散式檔案系統的備份恢復機制以及MapReduce的任務監控保證了分散式處理的可靠性。

高效:分散式檔案系統的高效資料互動實現以及MapReduce結合Local Data處理的模式,為高效處理海量的資訊作了基礎準備。

使用場景:個人覺得最適合的就是海量資料的分析,其實Google最早提出MapReduce也就是為了海量資料分析。同時HDFS最早是為了搜尋引擎實現而開發的,後來才被用於分散式計算框架中。海量資料被分割於多個節點,然後由每一個節點平行計算,將得出的結果歸併到輸出。同時第一階段的輸出又可以作為下一階段計算的輸入,因此可以想象到一個樹狀結構的分散式計算圖,在不同階段都有不同產出,同時並行和序列結合的計算也可以很好地在分散式叢集的資源下得以高效的處理。

 

文件型資料庫設計模式-如何儲存樹形資料

作者:nosqlfan on 星期二, 三月 8, 2011 · 8條評論 【閱讀:1,299 次】 

在資料庫中儲存樹形結構的資料,這是一個非常普遍的需求,典型的比如論壇系統的版塊關係。在傳統的關係型資料庫中,就已經產生了各種解決方案。

此文以儲存樹形結構資料為需求,分別描述了利用關係型資料庫和文件型資料庫作為儲存的幾種設計模式。

A.關係型資料庫設計模式1

id

name

parent_id

1

A

NULL

2

B

1

3

C

1

4

D

2

上圖表示了傳統的設計方法之一,就是將樹形結構的每一個結點作為關係型資料庫中的一行進行儲存,每一個結點儲存一個其父結點的指標。

  • 優點:結構簡單易懂,插入修改操作都很簡單
  • 缺點:如果要獲取某個結點的所有子結點,將是一件很噁心的事

B.關係型資料庫設計模式2

id

name

parent_id

left

right

1

A

NULL

1

8

2

B

1

2

5

3

C

1

6

7

4

D

2

3

4

上圖在模式1的基礎上多了兩列,left和right,相當於btree中的左右分支,分別儲存了左右分支結點的最大值和最小值。

  • 優點:要查詢一個結點的子結點很容易,只需要做一個範圍查詢就行了(比如B節點的子結點,只需要查詢 id >=2 && id<=5)
  • 缺點:由於樹結構存在在這裡面了,所以新增或修改已存在結點將可能產生連鎖反應,操作過於複雜

C.文件型資料庫設計模式1

{
  "name": "A",
  "children": [
    {"name": "B", "children": [{"name": "D"}]},
    {"name": "C"}
  ]
}

將整個樹結構存成一個文件,文件結構既樹型結構,簡明易懂。

  • 優點:簡明易懂
  • 缺點:文件會越來越大,對所有結點的修改都集中到這一個文件中,併發操作受限

D.文件型資料庫設計模式2

{"_id": "A", "children": ["B", "C"]}
{"_id": "B", "children": ["D"]}
{"_id": "C"}
{"_id": "D"}

將每個結點的所有子結點存起來

  • 優點:結構簡單,查詢子結點方便
  • 缺點:查詢父結點會比較麻煩

E.文件型資料庫設計模式3

{
  "leaf": "A",
  "children": [
    {"leaf": "B", "children": [{"leaf": "D"}] },
    {"leaf": "C"}
  ]
}
{"_id": "A", ...}
{"_id": "B", ...}
{"_id": "C", ...}
{"_id": "D", ...}

充分利用文件型儲存schema-less的優點,先利用上面C方案存儲存一個大的樹形文件,再將每一個結點的其他資訊單獨儲存。

  • 優點:操作方便,結構上的操作可以直接操作大的樹形文件,資料上的操作也只需要操作單條資料
  • 缺點:對所有結點的修改都集中到這一個文件中,併發操作受限

10gen工程師講資料庫索引實現

MongoDB儘管在資料儲存上與傳統關聯式資料庫很不一樣,但是在索引構建上卻幾乎是與傳統關係型資料庫一致。主要是使用了作為索引結構。

Indexes in MongoDBare conceptually similar to those in RDBMSes like MySQL. You will want an indexin MongoDB in the same sort of situations where you would have wanted an indexin MySQL.

下面一篇文章是10gen工程師  所寫,他拿食譜舉例,講解了在資料庫系統中索引的基本實現,非常形像。本站簡單翻譯如下,更詳細的內容,請直接檢視原文:

原文連結:TheJoy Of Indexing

1.唯一索引

想像你要在一本沒有目錄的食譜上查詢某一道菜的做法,那麼你唯一的辦法可能就是從頭到尾把這本幾百頁的書看一遍,直到找到你想找的菜。

而一個快速的方法就是給菜譜加上一個目錄,目錄中有每一道菜的名字與相應頁數的對應,並且菜名是按字母順序排列的如下,這樣你就可以按首字母快速地找到你要找的菜譜的頁數了。(這個基本上對應的是唯一索引的形式)

舉例:

水餃
- 45
水煮肉片
- 4,011
水煮魚
- 9432.非唯一索引

但是如果我今天買了條魚,想查一下所有跟魚相關的做法,那麼用上面的索引就沒辦法了。於是出現下面一種索引構建方法,即將所有菜的材料構建目錄,這時候一個材料對應多道菜。

舉例:


- 3, 20, 42, 88, 103, 1,215…
豬肉
- 2, 47, 88, 89, 90, 275…
白菜
- 7, 9, 80, 81, 82, 83, 84…

3.聯合索引

下面還有另一種需求,如果我知道材料裡有魚,而我又知道這道菜的名字,就不用查詢到材料後去翻看後面幾個具體頁的菜再來選擇了。滿足這種需求的是聯合索引:


- 水煮魚
—- 1,215
- 紅燒帶魚
—- 88
- 酸菜魚
—- 103
豬肉
- 紅燒肉
—- 875
- 小炒肉
—- 89
- 回鍋肉
—- 47
白菜
- 白菜湯
—- 2,000
- 素炒白菜
—- 2,133
- 白菜燉豆腐
—- 1,050

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/28211342/viewspace-2148179/,如需轉載,請註明出處,否則將追究法律責任。

相關文章