MongoDB的使用


什么是MongoDB?

mongodb是一個基於分布式文件儲存的數據庫,由C++編寫。是一個文檔型數據庫,提供好的性能,領先的非關系型數據庫

MongoDB的儲存形式類似於python的字典,以{‘key’:‘value’}的形式儲存

mongoDB適用於那些場景?

1.網站數據量大

2,網站數據讀寫操作頻繁

3.價值較低

 

數據庫mysql和mongdb的區別?

SQL:mysql、Oracle、sqlserver、db2

  1. 高度事務性場景:銀行、會計、貿易,庫管,需要大量原子性操作

  1. 適合存儲結構化數據,如用戶的帳號、地址,預先定義明確的字段

  1. 數據價值高、對安全性要求高、穩定性要求高

  1. 需要持久化存儲的 "冷數據"(不需要經常讀寫的數據)

  1. 需要通過SQL語言做關聯查詢,比如join

6.這些數據的規模、增長的速度通常是可以預期的

NoSQL:

redis key:value(string、hash、set、zset、list)、

mongodb {"name" : "xiaoming", "age" : 18}, {}

mongodb:字典格式,支持分組、索引、主從備份、集群

  1. 靈活的數據結構,適合存儲非結構化數據,如文章、評論,需要事先設計 數據的增刪改 的字段

  1. 高度收縮性場景,社交網絡、熱點資訊,NoSQL數據庫通常具有無限(至少接近)伸縮性

  1. 處理 "熱數據"(經常需要讀寫的數據),這些數據通常用於模糊處理,如全文搜索、機器學習

  1. 這些數據是海量的,而且增長的速度是難以預期的,更容易擴展

  1. 按key獲取數據效率很高,但是對join或其他結構化查詢的支持就比較差

大型互聯網項目都會選用MySQL(或任何關系型數據庫) + NoSQL的組合方案

端口號:

mysql: 3306

redis: 6379

mongodb: 27017

mongodb安裝教程

關於mongodb的好處,優點之類的這里就不說了,唯一要講的一點就是mongodb中有三元素:數據庫,集合,文檔,其中“集合”

就是對應關系數據庫中的“表”,“文檔”對應“行”。

下載

MongoDB官網 ,我們發現有32bit和64bit,這個就要看你系統了,不過這里有兩點注意:

①:根據業界規則,偶數為“穩定版”(如:1.6.X,1.8.X),奇數為“開發版”(如:1.7.X,1.9.X),這兩個版本的區別相信大家都知道吧。

②:32bit的mongodb最大只能存放2G的數據,64bit就沒有限制。

我這里就下載"2.0.2版本,32bit“,ok,下載之后我就放到”E盤“,改下文件夾名字為”mongodb“。

啟動

①:啟動之前,我們要給mongodb指定一個文件夾,這里取名為”db",用來存放mongodb的數據。

img

②:微軟徽標+R,輸入cmd,首先找到“mongodb”的路徑,然后運行mongod開啟命令,同時用--dbpath指定數據存放地點為“db”文件夾。

img

③:最后要看下是否開啟成功,從圖中的信息中獲知,mongodb采用27017端口,那么我們就在瀏覽器里面鍵入“http://localhost:27017/”,

打開后,mongodb告訴我們在27017上Add 1000可以用http模式查看mongodb的管理信息。

img

基本操作

由於是開篇,就大概的說下基本的“增刪查改“,我們再開一個cmd,輸入mongo命令打開shell,其實這個shell就是mongodb的客戶端,

同時也是一個js的編譯器,默認連接的是“test”數據庫。

img

insert 操作

好,數據庫有了,下一步就是集合,這里就取集合名為“person”,要注意的就是文檔是一個json的擴展(Bson)形式。

img

find 操作

我們將數據插入后,肯定是要find出來,不然插了也白插,這里要注意兩點:

① “_id": 這個字段是數據庫默認給我們加的GUID,目的就是保證數據的唯一性。

② 嚴格的按照Bson的形式書寫文檔,不過也沒關系,錯誤提示還是很強大的。

img

update操作

update方法的第一個參數為“查找的條件”,第二個參數為“更新的值”,學過C#,相信還是很好理解的。

img

remove操作

remove中如果不帶參數將刪除所有數據,呵呵,很危險的操作,在mongodb中是一個不可撤回的操作,三思而后行。

img

細說增刪改查

有一天當我們用上一篇同樣的方式打開mongodb,突然

傻眼了,擦,竟然開啟不了,仔細觀察“划線區域“的信息,發現db文件夾下有一個類似的”lock file”阻止了mongodb的開啟,接下來我們要做的就

是干掉它,之后,開啟成功,關於mongodb的管理方式將在后續文章分享。

img

一: Insert操作

上一篇也說過,文檔是采用“K-V”格式存儲的,如果大家對JSON比較熟悉的話,我相信學mongodb是手到擒來,我們知道JSON里面Value

可能是“字符串”,可能是“數組”,又有可能是內嵌的一個JSON對象,相同的方式也適合於BSON。

常見的插入操作也就兩種形式存在:“單條插入”和“批量插入”。

① 單條插入

先前也說了,mongo命令打開的是一個javascript shell。所以js的語法在這里面都行得通,看起來是不是很牛X。

img

② 批量插入

這玩意跟“單條插入”的差異相信大家應該知道,由於mongodb中沒有提供給shell的“批量插入方法”,沒關系,各個語言的driver都打通

了跟mongodb內部的批量插入方法,因為該方法是不可或缺的,如果大家非要模擬下批量插入的話,可以自己寫了for循環,里面就是insert。

二:Find操作

日常開發中,我們玩查詢,玩的最多的也就是二類:

①: >, >=, <, <=, !=, =。

②:And,OR,In,NotIn

這些操作在mongodb里面都封裝好了,下面就一一介紹:

<1>"$gt", "$gte", "$lt", "$lte", "$ne", "沒有特殊關鍵字",這些跟上面是一一對應的,舉幾個例子。

img

<2> "無關鍵字“, "$or", "$in","$nin" 同樣我也是舉幾個例子

img

<3> 在mongodb中還有一個特殊的匹配,那就是“正則表達式”,這玩意威力很強的。

img

<4> 有時查詢很復雜,很蛋疼,不過沒關系,mongodb給我們祭出了大招,它就是$where,為什么這么說,是因為$where中的value

就是我們非常熟悉,非常熱愛的js來助我們一馬平川。

img

三:Update操作

更新操作無非也就兩種,整體更新和局部更新,使用場合相信大家也清楚。

<1> 整體更新

不知道大家可還記得,我在上一篇使用update的時候,其實那種update是屬於整體更新。

img

<2> 局部更新

有時候我們僅僅需要更新一個字段,而不是整體更新,那么我們該如何做呢?easy的問題,mongodb中已經給我們提供了兩個

修改器: $inc 和 $set。

① $inc修改器

$inc也就是increase的縮寫,學過sql server 的同學應該很熟悉,比如我們做一個在線用戶狀態記錄,每次修改會在原有的基礎上

自增$inc指定的值,如果“文檔”中沒有此key,則會創建key,下面的例子一看就懂。

img

② $set修改器

啥也不說了,直接上代碼

img

<3> upsert操作

這個可是mongodb創造出來的“詞”,大家還記得update方法的第一次參數是“查詢條件”嗎?,那么這個upsert操作就是說:如果我

沒有查到,我就在數據庫里面新增一條,其實這樣也有好處,就是避免了我在數據庫里面判斷是update還是add操作,使用起來很簡單

將update的第三個參數設為true即可。

img

<4> 批量更新

在mongodb中如果匹配多條,默認的情況下只更新第一條,那么如果我們有需求必須批量更新,那么在mongodb中實現也是很簡單

的,在update的第四個參數中設為true即可。例子就不舉了。

四: Remove操作

這個操作在上一篇簡單的說過,這里就不贅述了。

細說高級操作

今天跟大家分享一下mongodb中比較好玩的知識,主要包括:聚合,游標。

一: 聚合

常見的聚合操作跟sql server一樣,有:count,distinct,group,mapReduce。

count

count是最簡單,最容易,也是最常用的聚合工具,它的使用跟我們C#里面的count使用簡直一模一樣。

img

distinct

這個操作相信大家也是非常熟悉的,指定了誰,誰就不能重復,直接上圖。

img

group

在mongodb里面做group操作有點小復雜,不過大家對sql server里面的group比較熟悉的話還是一眼

能看的明白的,其實group操作本質上形成了一種“k-v”模型,就像C#中的Dictionary,好,有了這種思維,

我們來看看如何使用group。

下面舉的例子就是按照age進行group操作,value為對應age的姓名。下面對這些參數介紹一下:

key: 這個就是分組的key,我們這里是對年齡分組。

initial: 每組都分享一個”初始化函數“,特別注意:是每一組,比如這個的age=20的value的list分享一個

initial函數,age=22同樣也分享一個initial函數。

$reduce: 這個函數的第一個參數是當前的文檔對象,第二個參數是上一次function操作的累計對象,第一次

為initial中的{”perosn“:[]}。有多少個文檔, $reduce就會調用多少次。

img

看到上面的結果,是不是有點感覺,我們通過age查看到了相應的name人員,不過有時我們可能有如下的要求:

①:想過濾掉age>25一些人員。

②:有時person數組里面的人員太多,我想加上一個count屬性標明一下。

針對上面的需求,在group里面還是很好辦到的,因為group有這么兩個可選參數: condition 和 finalize。

condition: 這個就是過濾條件。

finalize:這是個函數,每一組文檔執行完后,多會觸發此方法,那么在每組集合里面加上count也就是它的活了。

img

 

mapReduce

這玩意算是聚合函數中最復雜的了,不過復雜也好,越復雜就越靈活。

mapReduce其實是一種編程模型,用在分布式計算中,其中有一個“map”函數,一個”reduce“函數。

① map:

這個稱為映射函數,里面會調用emit(key,value),集合會按照你指定的key進行映射分組。

② reduce:

這個稱為簡化函數,會對map分組后的數據進行分組簡化,注意:在reduce(key,value)中的key就是

emit中的key,vlaue為emit分組后的emit(value)的集合,這里也就是很多{"count":1}的數組。

③ mapReduce:

這個就是最后執行的函數了,參數為map,reduce和一些可選參數。具體看圖可知:

img

從圖中我們可以看到如下信息:

result: "存放的集合名“;

input:傳入文檔的個數。

emit:此函數被調用的次數。

reduce:此函數被調用的次數。

output:最后返回文檔的個數。

最后我們看一下“collecton”集合里面按姓名分組的情況。

img

游標

mongodb里面的游標有點類似我們說的C#里面延遲執行,比如:

var list=db.person.find();

針對這樣的操作,list其實並沒有獲取到person中的文檔,而是申明一個“查詢結構”,等我們需要的時候通過

for或者next()一次性加載過來,然后讓游標逐行讀取,當我們枚舉完了之后,游標銷毀,之后我們在通過list獲取時,

發現沒有數據返回了。

img

 

當然我們的“查詢構造”還可以搞的復雜點,比如分頁,排序都可以加進去。

var single=db.person.find().sort({"name",1}).skip(2).limit(2);

那么這樣的“查詢構造”可以在我們需要執行的時候執行,大大提高了不必要的花銷。

img

索引操作

好,今天分享下mongodb中關於索引的基本操作,我們日常做開發都避免不了要對程序進行性能優化,而程序的操作無非就是CURD,通常我們

又會花費50%的時間在R上面,因為Read操作對用戶來說是非常敏感的,處理不好就會被人唾棄,呵呵。

從算法上來說有5種經典的查找,具體的可以參見我的算法速成系列,這其中就包括我們今天所說的“索引查找”,如果大家對sqlserver比較了解

的話,相信索引查找能給我們帶來什么樣的性能提升吧。

我們首先插入10w數據,上圖說話:

img

一:性能分析函數(explain)

好了,數據已經插入成功,既然我們要做分析,肯定要有分析的工具,幸好mongodb中給我們提供了一個關鍵字叫做“explain",那么怎么用呢?

還是看圖,注意,這里的name字段沒有建立任何索引,這里我就查詢一個“name10000”的姓名。

img

仔細看紅色區域,有幾個我們關心的key。

cursor: 這里出現的是”BasicCursor",什么意思呢,就是說這里的查找采用的是“表掃描”,也就是順序查找,很悲催啊。

nscanned: 這里是10w,也就是說數據庫瀏覽了10w個文檔,很恐怖吧,這樣玩的話讓人受不了啊。

n: 這里是1,也就是最終返回了1個文檔。

millis: 這個就是我們最最最....關心的東西,總共耗時114毫秒。

建立索引(ensureIndex)

在10w條這么簡單的集合中查找一個文檔要114毫秒有一點點讓人不能接收,好,那么我們該如何優化呢?mongodb中給

我們帶來了索引查找,看看能不能讓我們的查詢一飛沖天.....

img

這里我們使用了ensureIndex在name上建立了索引。”1“:表示按照name進行升序,”-1“:表示按照name進行降序。

我的神啊,再來看看這些敏感信息。

cursor: 這里出現的是”BtreeCursor",這么牛X,mongodb采用B樹的結構來存放索引,索引名為后面的“name_1"。

nscanned: 我擦,數據庫只瀏覽了一個文檔就OK了。

n: 直接定位返回。

millis: 看看這個時間真的不敢相信,秒秒殺。

通過這個例子相信大家對索引也有了感官方面的認識了吧。

唯一索引

和sqlserver一樣都可以建立唯一索引,重復的鍵值自然就不能插入,在mongodb中的使用方法是:

db.person.ensureIndex({"name":1},{"unique":true})。

img

組合索引

有時候我們的查詢不是單條件的,可能是多條件,比如查找出生在‘1989-3-2’名字叫‘jack’的同學,那么我們可以建立“姓名”和"生日“

的聯合索引來加速查詢。

img

看到上圖,大家或者也知道name跟birthday的不同,建立的索引也不同,升序和降序的順序不同都會產生不同的索引,

那么我們可以用getindexes來查看下person集合中到底生成了那些索引。

img

 

此時我們肯定很好奇,到底查詢優化器會使用哪個查詢作為操作,呵呵,還是看看效果圖:

img

看完上圖我們要相信查詢優化器,它給我們做出的選擇往往是最優的,因為我們做查詢時,查詢優化器會使用我們建立的這些索引來創建查詢方案,

如果某一個先執行完則其他查詢方案被close掉,這種方案會被mongodb保存起來,當然如果非要用自己指定的查詢方案,這也是

可以的,在mongodb中給我們提供了hint方法讓我們可以暴力執行。

img

 

刪除索引

可能隨着業務需求的變化,原先建立的索引可能沒有存在的必要了,可能有的人想說沒必要就沒必要唄,但是請記住,索引會降低CUD這三

種操作的性能,因為這玩意需要實時維護,所以啥問題都要綜合考慮一下,這里就把剛才建立的索引清空掉來演示一下:dropIndexes的使用。

img

主從復制

開始我們主要討論mongodb的部署技術。

我們知道sql server能夠做到讀寫分離,雙機熱備份和集群部署,當然mongodb也能做到,實際應用中我們不希望數據庫采用單點部署,

如果碰到數據庫宕機或者被毀滅性破壞那是多么的糟糕。

一:主從復制

1: 首先看看模型圖

img

 

2: 從上面的圖形中我們可以分析出這種架構有如下的好處:

<1> 數據備份。

<2> 數據恢復。

<3> 讀寫分離。

3:下面我們就一一實踐

實際應用中我們肯定是多服務器部署,限於自己懶的裝虛擬機,就在一台機器上實踐了。

第一步:我們把mongodb文件夾放在D盤和E盤,模擬放在多服務器上。

第二步:啟動D盤上的mongodb,把該數據庫指定為主數據庫,其實命令很簡單:>mongodb --dbpath='XXX' --master,

端口還是默認的27017.

img

第三步:同樣的方式啟動E盤上的mongodb,指定該數據庫為從屬數據庫,命令也很簡單,當然我們要換一個端口,比如:8888。

source 表示主數據庫的地址。

>mongod --dbpath=xxxx --port=8888 --slave --source=127.0.0.1:27017

img

第四步:從圖中的紅色區域我們發現了一條:“applied 1 operations"這樣的語句,並且發生的時間相隔10s,也就說明從屬數據庫每10s

就向主數據庫同步數據,同步依據也就是尋找主數據庫的”OpLog“日志,可以在圖中紅色區域內發現”sync_pullOpLog“字樣。

接下來我們要做的就是測試,驚訝的發現數據已經同步更新,爽啊。

img

 

4: 如果我還想增加一台從屬數據庫,但是我不想在啟動時就指定,而是后期指定,那么mongodb可否做的到呢?答案肯定是可以的。

我們的主或者從屬數據庫中都有一個叫做local的集合,主要是用於存放內部復制信息。

好,那么我們就試一下,我在F盤再拷貝一份mongodb的運行程序,cmd窗口好多啊,大家不要搞亂了。

img

看上面的log,提示沒有主數據庫,沒關系,某一天我們良心發現,給他后期補貼一下,哈哈,再開一個cmd窗口,語句也就是

在sources中add一個host地址,最后發現數據也同步到127.0.0.1:5555這台從屬數據庫中....

img

5: 讀寫分離

這種手段在大一點的架構中都有實現,在mongodb中其實很簡單,在默認的情況下,從屬數據庫不支持數據的讀取,但是沒關系,

在驅動中給我們提供了一個叫做“slaveOkay"來讓我們可以顯示的讀取從屬數據庫來減輕主數據庫的性能壓力,這里就不演示了。

二:副本集

這個也是很牛X的主從集群,不過跟上面的集群還是有兩點區別的。

<1>: 該集群沒有特定的主數據庫。

<2>: 如果哪個主數據庫宕機了,集群中就會推選出一個從屬數據庫作為主數據庫頂上,這就具備了自動故障恢復功能,很牛X的啊。

好,我們現在就來試一下,首先把所有的cmd窗口關掉重新來,清掉db下的所有文件。

第一步: 既然我們要建立集群,就得取個集群名字,這里就取我們的公司名shopex, --replSet表示讓服務器知道shopex下還有其他數據庫,

這里就把D盤里面的mongodb程序打開,端口為2222。指定端口為3333是shopex集群下的另一個數據庫服務器。

img

第二步: 既然上面說3333是另一個數據庫服務器,不要急,現在就來開,這里把E盤的mongodb程序打開。

img

第三步: ok,看看上面的日志紅色區域,似乎我們還沒有做完,是的,log信息告訴我們要初始化一下“副本集“,既然日志這么說,那我也就

這么做,隨便連接一下哪個服務器都行,不過一定要進入admin集合。

img

第四步: 開啟成功后,我們要看看誰才能成為主數據庫服務器,可以看到端口為2222的已經成為主數據庫服務器。

img

第五步:我們知道sql server里面有一個叫做仲裁服務器,那么mongodb中也是有的,跟sql server一樣,仲裁只參與投票選舉,這里我們

把F盤的mongodb作為仲裁服務器,然后指定shopex集群中的任一個服務器端口,這里就指定2222。

img

然后我們在admin集合中使用rs.addArb()追加即可。

img

追加好了之后,我們使用rs.status()來查看下集群中的服務器狀態,圖中我們可以清楚的看到誰是主,還是從,還是仲裁。

img

 

不是說該集群有自動故障恢復嗎?那么我們就可以來試一下,在2222端口的cmd服務器按Ctrl+C來KO掉該服務器,立馬我們發現

在3333端口的從屬服務器即可頂上,最后大家也可以再次使用rs.status()來看下集群中服務器的狀態。

img

分片技術

在mongodb里面存在另一種集群,就是分片技術,跟sql server的表分區類似,我們知道當數據量達到T級別的時候,我們的磁盤,內存

就吃不消了,針對這樣的場景我們該如何應對。

一:分片

mongodb采用將集合進行拆分,然后將拆分的數據均攤到幾個片上的一種解決方案。

img

 

下面我對這張圖解釋一下:

人臉: 代表客戶端,客戶端肯定說,你數據庫分片不分片跟我沒關系,我叫你干啥就干啥,沒什么好商量的。

mongos: 首先我們要了解”片鍵“的概念,也就是說拆分集合的依據是什么?按照什么鍵值進行拆分集合....

好了,mongos就是一個路由服務器,它會根據管理員設置的“片鍵”將數據分攤到自己管理的mongod集群,數據

和片的對應關系以及相應的配置信息保存在"config服務器"上。

mongod: 一個普通的數據庫實例,如果不分片的話,我們會直接連上mongod。

 

二: 實戰

首先我們准備4個mongodb程序,我這里是均攤在C,D,E,F盤上,當然你也可以做多個文件夾的形式。

1:開啟config服務器

先前也說了,mongos要把mongod之間的配置放到config服務器里面,理所當然首先開啟它,我這里就建立2222端口。

img

 

2: 開啟mongos服務器

這里要注意的是我們開啟的是mongos,不是mongod,同時指定下config服務器,這里我就開啟D盤上的mongodb,端口3333。

img

3:啟動mongod服務器

對分片來說,也就是要添加片了,這里開啟E,F盤的mongodb,端口為:4444,5555。

img

4: 服務配置

哈哈,是不是很興奮,還差最后一點配置我們就可以大功告成。

<1> 先前圖中也可以看到,我們client直接跟mongos打交道,也就說明我們要連接mongos服務器,然后將4444,5555的mongod

交給mongos,添加分片也就是addshard()。

img

這里要注意的是,在addshard中,我們也可以添加副本集,這樣能達到更高的穩定性。

<2>片已經集群了,但是mongos不知道該如何切分數據,也就是我們先前所說的片鍵,在mongodb中設置片鍵要做兩步

①:開啟數據庫分片功能,命令很簡單 enablesharding(),這里我就開啟test數據庫。

②:指定集合中分片的片鍵,這里我就指定為person.name字段。

img

5: 查看效果

好了,至此我們的分片操作全部結束,接下來我們通過mongos向mongodb插入10w記錄,然后通過printShardingStatus命令

查看mongodb的數據分片情況。

img

這里主要看三點信息:

① shards: 我們清楚的看到已經別分為兩個片了,shard0000和shard0001。

② databases: 這里有個partitioned字段表示是否分區,這里清楚的看到test已經分區。

③ chunks: 這個很有意思,我們發現集合被砍成四段:

無窮小 —— jack0,jack0 ——jack234813,jack234813——jack9999,jack9999——無窮大。

分區情況為:3:1,從后面的 on shardXXXX也能看得出。

運維技術

我們以管理員的視角來看mongodb,作為一名管理員,我們經常接觸到的主要有4個方面:

1. 安裝部署

2. 狀態監控

3. 安全認證

4. 備份和恢復,

下面我們就一點一點的講解。

一:安裝部署

我之前的文章都是采用console程序來承載,不過在生產環境中這並不是最佳實踐,誰也不願意在機器重啟后滿地找牙似找mongodb,

在mongodb里面提供了一個叫做“服務寄宿”的模式,我想如果大家對wcf比較熟悉的話很容易聽懂。好了,我們實踐一下,這里我開一下D盤

里面的mongodb。

 

img

這里要注意的有兩點:

<1> logpath: 當我們使用服務寄宿的時候,用眼睛都能想明白肯定不會用console來承載日志信息了。

<2> install: 開啟安裝服務寄宿,很happy啊,把管理員的手工操作降低到最小,感謝mongodb。

好了,console程序叫我看log日志,那我就看看,發現mongodb已經提示我們如何開啟mongodb,接着我照做就是了。

img

 

還要提醒大家一點的就是,這些命令參數很多很復雜也就很容易忘,不過沒關系,數據庫給我們提供了一個help方法,我們可以

拿mongod和mongo說事。

mongod:

img

mongo:

img

二:狀態監控

監控可以讓我們實時的了解數據庫的健康狀況以及性能調優,在mongodb里面給我們提供了三種方式。

1:http監視器

這個我在先前的文章中也提到了,這里就不贅述了。

2:serverStatus()

這個函數可以獲取到mongodb的服務器統計信息,其中包括 :全局鎖,索引,用戶操作行為等等這些統計信息,對管理員來說非常

重要,具體的參數含義可以參考園友:http://www.cnblogs.com/xuegang/archive/2011/10/13/2210339.html

這里還是截個圖混個眼熟。

img

3:mongostat

前面那些統計信息再牛X,那也是靜態統計,不能讓我觀看實時數據變化,還好,mongodb里面提供了這里要說的mongodstat

監視器,這玩意會每秒刷新,在實際生產環境中大有用處,還是截張圖,很有意思,是不是感覺大軍壓境了。

img

三: 安全認證

作為數據庫軟件,我們肯定不想誰都可以訪問,為了確保數據的安全,mongodb也會像其他的數據庫軟件一樣可以采用用戶

驗證的方法,那么該怎么做呢?其實很簡單,mongodb提供了addUser方法,還有一個注意點就是如果在admin數據庫中添加

將會被視為“超級管理員”。

img

上面的admin用戶將會被視為超級管理員,“jack”用戶追加的第三個參數表示是否是“只讀用戶”,好了,該添加的我們都添加了,

我們第一次登錄時不是采用驗證模式,現在我們使用--reinstall重啟服務並以--auth驗證模式登錄。

img

好了,我們進入test集合翻翻數據看看情況,我們發現jack用戶始終都是沒有寫入的權限,不管是授權或者未授權。

img

四:備份和恢復

這玩意的重要性我想都不需要我來說了吧,這玩意要是搞不好會死人的,mongodb里面常用的手段有3種。

1: 直接copy

這個算是最簡單的了,不過要注意一點,在服務器運行的情況下直接copy是很有風險的,可能copy出來時,數據已經遭到

破壞,唯一能保證的就是要暫時關閉下服務器,copy完后重開。

2:mongodump和mongorestore

這個是mongo給我們提供的內置工具,很好用,能保證在不關閉服務器的情況下copy數據。

為了操作方便,我們先刪除授權用戶。

img

好了,我們轉入正題,這里我先在D盤建立一個backup文件夾用於存放test數據庫。

img

快看,數據已經備份過來了,太爽了,現在我們用mongorestore恢復過去,記住啊,它是不用關閉機器的。

img

提一點的就是 drop選項,這里是說我將test數據恢復之前先刪除原有數據庫里面的數據,同樣大家可以通過help查看。

3:主從復制

這個我在上上篇有所介紹,這里也不贅述了。

其實上面的1,2兩點都不能保證獲取數據的實時性,因為我們在備份的時候可能還有數據灌在內存中不出來,那么我們

想說能不能把數據暴力的刷到硬盤上,當然是可以的,mongodb給我們提供了fsync+lock機制就能滿足我們提的需求。

fsync+lock首先會把緩沖區數據暴力刷入硬盤,然后給數據庫一個寫入鎖,其他實例的寫入操作全部被阻塞,直到fsync

+lock釋放鎖為止。

這里就不測試了。

加鎖: db.runCommand({"fsync":1,"lock":1})

釋放鎖: db.$cmd.unlock.findOne()

驅動實踐

得要說說C#驅動對mongodb的操作,目前驅動有兩種:官方驅動和samus驅動,不過我個人還是喜歡后者,

因為提供了豐富的linq操作,相當方便。

官方驅動:https://github.com/mongodb/mongo-csharp-driver/downloads。下載后,還提供了一個酷似msdn的幫助文檔。

samus驅動:https://github.com/samus/mongodb-csharp/downloads

下面就具體看看samus驅動,https://github.com/samus/mongodb-csharp/blob/master/examples/Simple/Main.cs上面提供了

一個簡單的demo,大體上看看我們就知道怎么玩了。

一: 實踐

1:我們建立一個Person實體,MongoAlias特性表示取別名,這里的ID值將會覆蓋掉數據庫自動生成的_id。

#region 數據實體
    /// <summary>
/// 數據實體
/// </summary>
    public class Person
    {
        [MongoAlias("_id")]
        public string ID { get; set; }

        public string Name { get; set; }

        public int Age { get; set; }

        public DateTime CreateTime { get; set; }
    }
    #endregion

2:初始化一些變量

string connectionString = string.Empty;

        string databaseName = string.Empty;

        string collectionName = string.Empty;

        static MongodbHelper<T> mongodb;

        #region 初始化操作
/// <summary>
/// 初始化操作
/// </summary>
        public MongodbHelper()
        {
            connectionString = "Server=127.0.0.1:2222";
            databaseName = "shopex";
            collectionName = "person";
        }
        #endregion

3:為了方便T的繼承類使用linq功能,我們還需要映射一下。

#region 實現linq查詢的映射配置
        /// <summary>
/// 實現linq查詢的映射配置
/// </summary>
        public MongoConfiguration configuration
        {
            get
            {
                var config = new MongoConfigurationBuilder();

                config.Mapping(mapping =>
                {
                    mapping.DefaultProfile(profile =>
                    {
                        profile.SubClassesAre(t => t.IsSubclassOf(typeof(T)));
                    });
                    mapping.Map<T>();
                    mapping.Map<T>();
                });

                config.ConnectionString(connectionString);

                return config.BuildConfiguration();
            }
        }
        #endregion

4:下面是一些基本的CURD的代碼,跟寫EF代碼很類似,寫起來好舒服。

#region 插入操作
        /// <summary>
/// 插入操作
/// </summary>
/// <param name="person"></param>
/// <returns></returns>
        public void Insert(T t)
        {
            using (Mongo mongo = new Mongo(configuration))
            {
                try
                {
                    mongo.Connect();

                    var db = mongo.GetDatabase(databaseName);

                    var collection = db.GetCollection<T>(collectionName);

                    collection.Insert(t, true);

                    mongo.Disconnect();

                }
                catch (Exception)
                {
                    mongo.Disconnect();
                    throw;
                }
            }
        }
        #endregion

        #region 更新操作
        /// <summary>
/// 更新操作
/// </summary>
/// <param name="person"></param>
/// <returns></returns>
        public void Update(T t, Expression<Func<T, bool>> func)
        {
            using (Mongo mongo = new Mongo(configuration))
            {
                try
                {
                    mongo.Connect();

                    var db = mongo.GetDatabase(databaseName);

                    var collection = db.GetCollection<T>(collectionName);

                    collection.Update<T>(t, func, true);

                    mongo.Disconnect();

                }
                catch (Exception)
                {
                    mongo.Disconnect();
                    throw;
                }
            }
        }
        #endregion

        #region 獲取集合
        /// <summary>
///獲取集合
/// </summary>
/// <param name="person"></param>
/// <returns></returns>
        public List<T> List(int pageIndex, int pageSize, Expression<Func<T, bool>> func, out int pageCount)
        {
            pageCount = 0;

            using (Mongo mongo = new Mongo(configuration))
            {
                try
                {
                    mongo.Connect();

                    var db = mongo.GetDatabase(databaseName);

                    var collection = db.GetCollection<T>(collectionName);

                    pageCount = Convert.ToInt32(collection.Count());

                    var personList = collection.Linq().Where(func).Skip(pageSize * (pageIndex - 1))
                                                   .Take(pageSize).Select(i => i).ToList();

                    mongo.Disconnect();

                    return personList;

                }
                catch (Exception)
                {
                    mongo.Disconnect();
                    throw;
                }
            }
        }
        #endregion

        #region 讀取單條記錄
        /// <summary>
///讀取單條記錄
/// </summary>
/// <param name="person"></param>
/// <returns></returns>
        public T Single(Expression<Func<T, bool>> func)
        {
            using (Mongo mongo = new Mongo(configuration))
            {
                try
                {
                    mongo.Connect();

                    var db = mongo.GetDatabase(databaseName);

                    var collection = db.GetCollection<T>(collectionName);

                    var single = collection.Linq().FirstOrDefault(func);

                    mongo.Disconnect();

                    return single;

                }
                catch (Exception)
                {
                    mongo.Disconnect();
                    throw;
                }
            }
        }
        #endregion

        #region 刪除操作
        /// <summary>
/// 刪除操作
/// </summary>
/// <param name="person"></param>
/// <returns></returns>
        public void Delete(Expression<Func<T, bool>> func)
        {
            using (Mongo mongo = new Mongo(configuration))
            {
                try
                {
                    mongo.Connect();

                    var db = mongo.GetDatabase(databaseName);

                    var collection = db.GetCollection<T>(collectionName);

                    //這個地方要注意,一定要加上T參數,否則會當作object類型處理
//導致刪除失敗
                    collection.Remove<T>(func);

                    mongo.Disconnect();

                }
                catch (Exception)
                {
                    mongo.Disconnect();
                    throw;
                }
            }
        }
        #endregion

5. 好,我們開一下2222端口,由於前前篇我已經把這個mongodb做成了服務,現在就直接連過去了,並做一下對Name的索引。

img

6. 一切准備妥當,我們做下基本的操作,比如這里我添加一千條數據,注意我開啟的是安全模式,如果插入不成功,將會拋出異常。

<1> Add:

#region 插入操作
        /// <summary>
/// 插入操作
/// </summary>
/// <param name="person"></param>
/// <returns></returns>
        public void Insert(T t)
        {
            using (Mongo mongo = new Mongo(configuration))
            {
                try
                {
                    mongo.Connect();

                    var db = mongo.GetDatabase(databaseName);

                    var collection = db.GetCollection<T>(collectionName);

                    collection.Insert(t, true);

                    mongo.Disconnect();

                }
                catch (Exception)
                {
                    mongo.Disconnect();
                    throw;
                }
            }
        }
        #endregion

        #region 更新操作
        /// <summary>
/// 更新操作
/// </summary>
/// <param name="person"></param>
/// <returns></returns>
        public void Update(T t, Expression<Func<T, bool>> func)
        {
            using (Mongo mongo = new Mongo(configuration))
            {
                try
                {
                    mongo.Connect();

                    var db = mongo.GetDatabase(databaseName);

                    var collection = db.GetCollection<T>(collectionName);

                    collection.Update<T>(t, func, true);

                    mongo.Disconnect();

                }
                catch (Exception)
                {
                    mongo.Disconnect();
                    throw;
                }
            }
        }
        #endregion

        #region 獲取集合
        /// <summary>
///獲取集合
/// </summary>
/// <param name="person"></param>
/// <returns></returns>
        public List<T> List(int pageIndex, int pageSize, Expression<Func<T, bool>> func, out int pageCount)
        {
            pageCount = 0;

            using (Mongo mongo = new Mongo(configuration))
            {
                try
                {
                    mongo.Connect();

                    var db = mongo.GetDatabase(databaseName);

                    var collection = db.GetCollection<T>(collectionName);

                    pageCount = Convert.ToInt32(collection.Count());

                    var personList = collection.Linq().Where(func).Skip(pageSize * (pageIndex - 1))
                                                   .Take(pageSize).Select(i => i).ToList();

                    mongo.Disconnect();

                    return personList;

                }
                catch (Exception)
                {
                    mongo.Disconnect();
                    throw;
                }
            }
        }
        #endregion

        #region 讀取單條記錄
        /// <summary>
///讀取單條記錄
/// </summary>
/// <param name="person"></param>
/// <returns></returns>
        public T Single(Expression<Func<T, bool>> func)
        {
            using (Mongo mongo = new Mongo(configuration))
            {
                try
                {
                    mongo.Connect();

                    var db = mongo.GetDatabase(databaseName);

                    var collection = db.GetCollection<T>(collectionName);

                    var single = collection.Linq().FirstOrDefault(func);

                    mongo.Disconnect();

                    return single;

                }
                catch (Exception)
                {
                    mongo.Disconnect();
                    throw;
                }
            }
        }
        #endregion

        #region 刪除操作
        /// <summary>
/// 刪除操作
/// </summary>
/// <param name="person"></param>
/// <returns></returns>
        public void Delete(Expression<Func<T, bool>> func)
        {
            using (Mongo mongo = new Mongo(configuration))
            {
                try
                {
                    mongo.Connect();

                    var db = mongo.GetDatabase(databaseName);

                    var collection = db.GetCollection<T>(collectionName);

                    //這個地方要注意,一定要加上T參數,否則會當作object類型處理
//導致刪除失敗
                    collection.Remove<T>(func);

                    mongo.Disconnect();

                }
                catch (Exception)
                {
                    mongo.Disconnect();
                    throw;
                }
            }
        }
        #endregion

img

乍一看顯示的數據以為有問題,為什么沒有出現jack0或者jack999,不過find的一下后心情舒坦了。

<2> update: 這里就把jack941的名字改掉“mary”

static void Main(string[] args)
        {
            MongodbHelper<Person> helper = new MongodbHelper<Person>();

            //修改jack941改成mary
            var single = helper.Single(i => i.Name == "jack941");
            single.Name = "mary";
            helper.Update(single, i => i.ID == single.ID);

            Console.WriteLine("修改成功");
            Console.Read();
        }

img

<3>Delete: 刪除mary這條記錄

static void Main(string[] args)
        {
            MongodbHelper<Person> helper = new MongodbHelper<Person>();

            //刪除mary這個記錄
            helper.Delete(i => i.Name == "mary");

            Console.WriteLine("刪除成功");
            Console.Read();
        }

img

<4> list操作: 這里我獲取一下名字里面帶9的人數列表

static void Main(string[] args)
        {
            MongodbHelper<Person> helper = new MongodbHelper<Person>();

            int pagecount;

            //獲取名字里面帶9的人數
            var list = helper.List(1, 20, i => i.Name.Contains("9"), out pagecount);

           Console.Read();
        }

 

img

總的運行代碼

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;
using System.Linq.Expressions;

using MongoDB.Configuration;
using MongoDB.Linq;
using MongoDB.Attributes;


namespace MongoDB.Test
{
    public class MongodbHelper<T> where T : class
    {
        string connectionString = string.Empty;

        string databaseName = string.Empty;

        string collectionName = string.Empty;

        static MongodbHelper<T> mongodb;

        #region 初始化操作
        /// <summary>
/// 初始化操作
/// </summary>
        public MongodbHelper()
        {
            connectionString = "Server=127.0.0.1:2222";
            databaseName = "shopex";
            collectionName = "person";
        }
        #endregion

        #region 實現linq查詢的映射配置
        /// <summary>
/// 實現linq查詢的映射配置
/// </summary>
        public MongoConfiguration configuration
        {
            get
            {
                var config = new MongoConfigurationBuilder();

                config.Mapping(mapping =>
                {
                    mapping.DefaultProfile(profile =>
                    {
                        profile.SubClassesAre(t => t.IsSubclassOf(typeof(T)));
                    });
                    mapping.Map<T>();
                    mapping.Map<T>();
                });

                config.ConnectionString(connectionString);

                return config.BuildConfiguration();
            }
        }
        #endregion

        #region 插入操作
        /// <summary>
/// 插入操作
/// </summary>
/// <param name="person"></param>
/// <returns></returns>
        public void Insert(T t)
        {
            using (Mongo mongo = new Mongo(configuration))
            {
                try
                {
                    mongo.Connect();

                    var db = mongo.GetDatabase(databaseName);

                    var collection = db.GetCollection<T>(collectionName);

                    collection.Insert(t, true);

                    mongo.Disconnect();

                }
                catch (Exception)
                {
                    mongo.Disconnect();
                    throw;
                }
            }
        }
        #endregion

        #region 更新操作
        /// <summary>
/// 更新操作
/// </summary>
/// <param name="person"></param>
/// <returns></returns>
        public void Update(T t, Expression<Func<T, bool>> func)
        {
            using (Mongo mongo = new Mongo(configuration))
            {
                try
                {
                    mongo.Connect();

                    var db = mongo.GetDatabase(databaseName);

                    var collection = db.GetCollection<T>(collectionName);

                    collection.Update<T>(t, func, true);

                    mongo.Disconnect();

                }
                catch (Exception)
                {
                    mongo.Disconnect();
                    throw;
                }
            }
        }
        #endregion

        #region 獲取集合
        /// <summary>
///獲取集合
/// </summary>
/// <param name="person"></param>
/// <returns></returns>
        public List<T> List(int pageIndex, int pageSize, Expression<Func<T, bool>> func, out int pageCount)
        {
            pageCount = 0;

            using (Mongo mongo = new Mongo(configuration))
            {
                try
                {
                    mongo.Connect();

                    var db = mongo.GetDatabase(databaseName);

                    var collection = db.GetCollection<T>(collectionName);

                    pageCount = Convert.ToInt32(collection.Count());

                    var personList = collection.Linq().Where(func).Skip(pageSize * (pageIndex - 1))
                                                   .Take(pageSize).Select(i => i).ToList();

                    mongo.Disconnect();

                    return personList;

                }
                catch (Exception)
                {
                    mongo.Disconnect();
                    throw;
                }
            }
        }
        #endregion

        #region 讀取單條記錄
        /// <summary>
///讀取單條記錄
/// </summary>
/// <param name="person"></param>
/// <returns></returns>
        public T Single(Expression<Func<T, bool>> func)
        {
            using (Mongo mongo = new Mongo(configuration))
            {
                try
                {
                    mongo.Connect();

                    var db = mongo.GetDatabase(databaseName);

                    var collection = db.GetCollection<T>(collectionName);

                    var single = collection.Linq().FirstOrDefault(func);

                    mongo.Disconnect();

                    return single;

                }
                catch (Exception)
                {
                    mongo.Disconnect();
                    throw;
                }
            }
        }
        #endregion

        #region 刪除操作
        /// <summary>
/// 刪除操作
/// </summary>
/// <param name="person"></param>
/// <returns></returns>
        public void Delete(Expression<Func<T, bool>> func)
        {
            using (Mongo mongo = new Mongo(configuration))
            {
                try
                {
                    mongo.Connect();

                    var db = mongo.GetDatabase(databaseName);

                    var collection = db.GetCollection<T>(collectionName);

                    //這個地方要注意,一定要加上T參數,否則會當作object類型處理
//導致刪除失敗
                    collection.Remove<T>(func);

                    mongo.Disconnect();

                }
                catch (Exception)
                {
                    mongo.Disconnect();
                    throw;
                }
            }
        }
        #endregion
    }

    #region 數據實體
    /// <summary>
/// 數據實體
/// </summary>
    public class Person
    {
        [MongoAlias("_id")]
        public string ID { get; set; }

        public string Name { get; set; }

        public int Age { get; set; }

        public DateTime CreateTime { get; set; }
    }
    #endregion
}

 

 

 

img

 

作者: 一線碼農


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM