在花了不少時間研究學習了MongoDB資料庫的相關知識,以及利用C#對MongoDB資料庫的封裝、測試應用後,決定花一些時間來總結一下最近的研究心得,把這個資料庫的應用單獨作為一個系列來介紹,希望從各個方面來總結並記錄一下這個新型、看似神秘的資料庫使用過程。本文是這個系列的開篇,主要介紹一些Mong...
在花了不少時間研究學習了MongoDB資料庫的相關知識,以及利用C#對MongoDB資料庫的封裝、測試應用後,決定花一些時間來總結一下最近的研究心得,把這個資料庫的應用單獨作為一個系列來介紹,希望從各個方面來總結並記錄一下這個新型、看似神秘的資料庫使用過程。本文是這個系列的開篇,主要介紹一些MongoDB資料庫的基礎知識、安裝過程、基礎使用等方面。
MongoDB是一款由C++編寫的高性能、開源、無模式的常用非關係型資料庫產品,是非關係資料庫當中功能最豐富、最像關係資料庫的資料庫。它擴展了關係型資料庫的眾多功能,例如:輔助索引、範圍查詢、排序等。
MongoDB主要解決的是海量數據的訪問效率問題,它作為分散式數據崛起後,使用較多的一款非結構資料庫,必然有其值得稱道之處,它的主要功能特性如下:
1)面向集合的存儲,適合存儲對象及JSON形式的數據。
2)動態查詢,MongoDB支持豐富的查詢表達式。查詢指令使用JSON形式的標記,可輕易查詢文檔中內嵌的對象及數組。
3)完整的索引支持,包括文檔內嵌對象及數組。MongoDB的查詢優化器會分析查詢表達式,並生成一個高效的查詢計劃。
4)查詢監視,MongoDB包含一個監視工具用於分析資料庫操作的性能。
5)複製及自動故障轉移,MongoDB資料庫支持伺服器之間的數據複製,支持主-從模式及伺服器之間的相互複製。複製的主要目標是提供冗餘及自動故障轉移。
6)高效的傳統存儲方式,支持二進位數據及大型對象(如圖片或視頻)。
7)自動分片以支持雲級別的伸縮性,自動分片功能支持水平的資料庫集群,可動態添加額外的機器。
1、MongoDB資料庫和傳統關係資料庫的對比
MongoDB資料庫有幾個簡單的概念需要瞭解一下。
1)MongoDB中的 database
有著和我們熟知的"資料庫"一樣的概念 (對 Oracle 來說就是 schema)。一個 MongoDB 實例中,可以有零個或多個資料庫,每個都作為一個高等容器,用於存儲數據。
2)資料庫中可以有零個或多個 collections
(集合)。集合和傳統意義上的 table 基本一致,可以簡單的把兩者看成是一樣的東西。
3)集合是由零個或多個 documents
(文檔)組成。同樣,一個文檔可以看成是一 row
。
4)文檔是由零個或多個 fields
(欄位)組成。,對應的就是關係資料庫的 columns
。
5)Indexes
(索引)在 MongoDB 中扮演著和它們在 RDBMS 中一樣的角色,都是為了提高查詢的效率。
6)Cursors
(游標)和上面的五個概念都不一樣,但是它非常重要,並且經常被忽視,其中最重要的你要理解的一點是,游標是當你問 MongoDB 拿數據的時候,它會給你返回一個結果集的指針而不是真正的數據,這個指針我們叫它游標,我們可以拿游標做我們想做的任何事情,比如說計數或者跨行之類的,而無需把真正的數據拖下來,在真正的數據上操作。
它們的對比關係圖如下所示。
數據在Mongodb裡面都是以Json格式方式進行存儲的,如下所示是其中的一個記錄內容。
{ _id: ObjectID('4bd9e8e17cefd644108961bb'), name:'Vivek', class : '12th', subjects: [ 'physics', 'chemistry', 'math', 'english', 'computer'], address: { house_no: '12B', block: 'B', sector: 12, city : 'noida', }, grade: [ { exam: 'unit test 1', score: '60%' }, { exam: 'unit test 2', score: '70%' } ] }
在過去的很長一段時間中,關係型資料庫一直是最主流的資料庫解決方案,他運用真實世界中事物與關係來解釋資料庫中抽象的數據架構。然而,在信息技術爆炸式發展的今天,大數據已經成為了繼雲計算,物聯網後新的技術革命,關係型資料庫在處理大數據量時已經開始吃力,開發者只能通過不斷地優化資料庫來解決數據量的問題,但優化畢竟不是一個長期方案,所以人們提出了一種新的資料庫解決方案來迎接大數據時代的到來——NoSQL(非關係型資料庫),其中MongoDB資料庫就是其中的NoSQL的傑出代表。在大數據時代中,大數據量的處理已經成了考量一個資料庫最重要的原因之一。而MongoDB的一個主要目標就是儘可能的讓資料庫保持卓越的性能,這很大程度地決定了MongoDB的設計。
根據MongoDB官網的說明,MongoDB的適用場景如下:
1)網站實時數據:MongoDB非常適合實時的插入,更新與查詢,並具備網站實時數據存儲所需的複製及高度伸縮性。
2)數據緩存:由於性能很高,MongoDB也適合作為信息基礎設施的緩存層。在系統重啟之後,由MongoDB搭建的持久化緩存層可以避免下層的數據源過載。
3)大尺寸、低價值數據存儲:使用傳統的關係型資料庫存儲一些數據時可能會比較昂貴,在此之前,很多時候程式員往往會選擇傳統的文件進行存儲。
4)高伸縮性場景:MongoDB非常適合由數十或數百台伺服器組成的資料庫。MongoDB的路線圖中已經包含對MapReduce引擎的內置支持。
5)對象或JSON數據存儲:MongoDB的BSON數據格式非常適合文檔化格式的存儲及查詢。
MongoDB不適合使用場景如下:
1)高度事務性系統:例如銀行或會計系統。傳統的關係型資料庫目前還是更適用於需要大量原子性複雜事務的應用程式。
2)傳統的商業智能應用:針對特定問題的BI資料庫會對產生高度優化的查詢方式。
3)需要複雜SQL查詢的問題。
MongoDB大多數情況下,可以代替關係型資料庫實現資料庫業務。它更簡單更直接、更快速並且通常對應用開發者的約束更少,不過缺乏事務支持需要慎重考慮業務需要。
2、MongoDB資料庫的安裝及基礎使用
MongoDB數據的官網為:https://www.mongodb.org/,當前版本為3.2,可以直接下載安裝版本在Linux或者Windows進行安裝。
一般在Windows,我們預設安裝的路徑為C:\Program Files\MongoDB,安裝後可以手動創建一個放置資料庫和日誌文件的目錄,一般不要放在C盤就好,如下所示:
創建文件夾d:\mongodb\data\db、d:\mongodb\data\log,分別用來安裝db和日誌文件,我們以後運行資料庫後,這個目錄就用來放置我們創建的資料庫和日誌資源了。
一般我們安裝後,為了在命令行方便調用Mongodb的命令,我們可以設置一個全局的路徑變數,如下所示。
預設情況下,mongodb的工作模式,是啟動一個DOS視窗,運行mongodb的資料庫服務,一旦這個DOS視窗關閉,也就停止了相關的服務,在Windows平臺,我們可以把它寄宿在Windows服務裡面,讓它隨著系統的啟動而啟動,也不必因為誤關閉視窗而停止了資料庫服務了。
通過下麵命令行執行資料庫服務的處理。
mongod --dbpath "d:\mongodb\data\db" --logpath "d:\mongodb\data\log\MongoDB.log" --install --serviceName "MongoDB"
然後使用命令行啟動服務
NET START MongoDB
創建服務並順利啟動成功後,然後就可以在系統的服務列表裡查看到了,我們確認把它設置為自動啟動的Windows服務即可。
啟動後,我們可以在系統【運行】裡面直接使用命令mongo打開視窗就可以進行相關的操作了。
上面用了一些常見的命令操作。
- show dbs 顯示資料庫列表
- use dbname 進入dbname資料庫,大小寫敏感,沒有這個資料庫也不要緊
- show collections 顯示資料庫中的集合,相當於表格
- db.<collection_name>.find(); 集合查找方法,參考上面的方式,使用pretty()函數是排版更好看的意思。
而其中find方法很強大,可以組合很多條件查詢的方式,如下所示:
- db.collection.find({ "key" : value }) 查找key=value的數據
- db.collection.find({ "key" : { $gt: value } }) key > value
- db.collection.find({ "key" : { $lt: value } }) key < value
- db.collection.find({ "key" : { $gte: value } }) key >= value
- db.collection.find({ "key" : { $lte: value } }) key <= value
- db.collection.find({ "key" : { $gt: value1 , $lt: value2 } }) value1 < key <value2
- db.collection.find({ "key" : { $ne: value } }) key <> value
- db.collection.find({ "key" : { $mod : [ 10 , 1 ] } }) 取模運算,條件相當於key % 10 == 1 即key除以10餘數為1的
- db.collection.find({ "key" : { $nin: [ 1, 2, 3 ] } }) 不屬於,條件相當於key的值不屬於[ 1, 2, 3 ]中任何一個
- db.collection.find({ "key" : { $in: [ 1, 2, 3 ] } }) 屬於,條件相當於key等於[ 1, 2, 3 ]中任何一個
- db.collection.find({ "key" : { $size: 1 } }) $size 數量、尺寸,條件相當於key的值的數量是1(key必須是數組,一個值的情況不能算是數量為1的數組)
- db.collection.find({ "key" : { $exists : true|false } }) $exists 欄位存在,true返回存在欄位key的數據,false返回不存在字度key的數據
- db.collection.find({ "key": /^val.*val$/i }) 正則,類似like;“i”忽略大小寫,“m”支持多行
- db.collection.find({ $or : [{a : 1}, {b : 2} ] }) $or或 (註意:MongoDB 1.5.3後版本可用),符合條件a=1的或者符合條件b=2的數據都會查詢出來
- db.collection.find({ "key": value , $or : [{ a : 1 } , { b : 2 }] }) 符合條件key=value ,同時符合其他兩個條件中任意一個的數據
- db.collection.find({ "key.subkey" :value }) 內嵌對象中的值匹配,註意:"key.subkey"必須加引號
- db.collection.find({ "key": { $not : /^val.*val$/i } }) 這是一個與其他查詢條件組合使用的操作符,不會單獨使用。上述查詢條件得到的結果集加上$not之後就能獲得相反的集合。
當然還有插入更新的處理語句也是很特別的。
db.student.insert({name:'student1',subject:['arts','music']})
特別是更新操作需要說明一下,支持常規的$set方法(修改)、$unset方法(移除指定的鍵),還有原子級的$inc方法(數值增減),$rename方法(重命名欄位名稱)等等,
db.users.update({"_id" : ObjectId("51826852c75fdd1d8b805801")}, {"$set" : {"hobby" :["swimming","basketball"]}} )
db.users.update({"_id" : ObjectId("51826852c75fdd1d8b805801")},{"$unset" : {"hobby" :1 }} )
db.posts.update({"_id" : ObjectId("5180f1a991c22a72028238e4")}, {"$inc":{"pageviews":1}})
db.students.update( { _id: 1 }, { $rename: { 'nickname': 'alias', 'cell': 'mobile' } }
upsert是一種特殊的更新操作,不是一個操作符。(upsert = up[date]+[in]sert),也就是如果存在則更新,否則就寫入一條新的記錄操作。這個參數是個布爾類型,預設是false。
db.users.update({age :25}, {$inc :{"age" :3}}, true)
另外,Update可以對Json的集合進行處理,如果對於subject對象是一個集合的話,插入或更新其中的欄位使用下麵的語句
db.student.update({name:'student5'},{$set:{subject:['music']}},{upsert:true});
如果是記錄已經存在,我們可以使用索引數值進行更新其中集合裡面的數據,如下所示。
db.student.update({name:'student3'},{$set:{'subject.0':'arts'}});
如果我們先在集合裡面增加一個記錄,而非替換的話,那麼使用$push語句,如下麵的語句所示。
db.student.update({name:'student3'},{$push:{'subject':'sports'}})
相反,如果要移除集合裡面的某個值,使用$pop操作符,那麼語句如下所示
db.student.update({name:'student3'},{$pop:{'subject':1}});
其中索引為1標識最右邊的記錄,-1標識為最左邊的記錄。
另外還可以使用$pushAll
和$pullAll來增加/移除一個或多個集合記錄,如下代碼所示。
db.student.update({name:'student3'},{$pushAll:{'subject':['sports','craft']}}) db.student.update({name:'student3'},{$pullAll:{'subject':['sports','craft']}})
mongodb的資料庫的操作還是比較容易理解的,具體可以進一步參考官網裡面的介紹。
https://docs.mongodb.org/manual/
https://docs.mongodb.org/getting-started/csharp/client/
http://mongodb.github.io/mongo-csharp-driver/2.2/
http://wiki.jikexueyuan.com/project/the-little-mongodb-book/
3、MongoDB資料庫的C#驅動的使用
資料庫的C#驅動使用介紹,可以參考:https://docs.mongodb.org/getting-started/csharp/,或者http://mongodb.github.io/mongo-csharp-driver/2.2/,可以下載相關的DLL然後在項目中引用,當前的驅動版本為2.2,一般引入下麵幾個DLL即可。
- MongoDB.Bson.dll
- MongoDB.Driver.dll
- MongoDB.Driver.Core.dll
也可以使用VS工具的NugGet包進行下載管理,如下所示。
然後在彈出的NugGet程式包管理界面裡面搜索mongo,然後添加MongoDB.Driver的資料庫驅動就可以使用了。
MongoDB資料庫驅動在2.2版本(或者是從2.0開始)好像完全改寫了API的介面,因此目前這個版本同時支持兩個版本的API處理,一個是基於MongoDatabase的對象介面,一個是IMongoDatabase的對象介面,前者中規中矩,和我們使用Shell裡面的命令名稱差不多,後者IMongoDatabase的介面是基於非同步的,基本上和前者差別很大,而且介面都提供了非同步的處理操作。後面我會分別對這兩個部分進行詳細的介紹,本文基於篇幅的原因,介紹一下兩者的簡單差異就可以了。
我們以Mongodb的資料庫連接字元串mongodb://localhost/local來進行構建
1)舊介面MongoDatabase對象的構建
var client = new MongoClient(connectionString); var database = client.GetServer().GetDatabase(new MongoUrl(connectionString).DatabaseName);
2)新介面IMongoDatabase對象的構建
var client = new MongoClient(connectionString); var database = client.GetDatabase(new MongoUrl(connectionString).DatabaseName);
後者已經沒有了GetServer的介面了。
3)舊介面的查找對象處理
/// <summary> /// 查詢資料庫,檢查是否存在指定ID的對象 /// </summary> /// <param name="key">對象的ID值</param> /// <returns>存在則返回指定的對象,否則返回Null</returns> public virtual T FindByID(string id) { ArgumentValidation.CheckForEmptyString(id, "傳入的對象id為空"); MongoCollection<T> collection = GetCollection(); return collection.FindOneById(new ObjectId(id)); }
3)新介面查找對象的處理
/// <summary> /// 查詢資料庫,檢查是否存在指定ID的對象 /// </summary> /// <param name="key">對象的ID值</param> /// <returns>存在則返回指定的對象,否則返回Null</returns> public virtual T FindByID(string id) { ArgumentValidation.CheckForEmptyString(id, "傳入的對象id為空"); IMongoCollection<T> collection = GetCollection(); return collection.Find(s=> s.Id == id).FirstOrDefault(); }
新介面已經沒有了FindOneById等介面了,所有的操作基本上都通過Find方法進行處理。舊介面很多通過Query對象進行條件的查詢,新介面又換了一個對象進行過濾條件處理了,總的來說,兩個介面差異非常大。
例如舊版本介面的Query使用C#代碼如下所示:
private void TestQuery() { Query.All("name", new List<BsonValue> { BsonValue.Create("a"), BsonValue.Create("b") });//通過多個元素來匹配數組 Query.And(Query.EQ("name", "a"), Query.EQ("title", "t"));//同時滿足多個條件 Query.Or(Query.EQ("name", "a"), Query.EQ("title", "t"));//滿足其中一個條件 Query.EQ("name", "a");//等於 Query.Exists("type");//判斷鍵值是否存在 Query.GT("value", 2);//大於> Query.GTE("value", 3);//大於等於>= Query.In("name", new List<BsonValue> { BsonValue.Create("a"), BsonValue.Create("b") });//包括指定的所有值,可以指定不同類型的條件和值 Query.LT("value", 9);//小於< Query.LTE("value", 8);//小於等於<= Query.Mod("value", 3, 1);//將查詢值除以第一個給定值,若餘數等於第二個給定值則返回該結果 Query.NE("name", "c");//不等於 Query.Size("name", 2);//給定鍵的長度 Query.Type("_id", BsonType.ObjectId);//給定鍵的類型 Query.ElemMatch("children", Query.And( Query.EQ("name", "C3"), Query.EQ("value", "C"))); //Query.Nor(Array);//不包括數組中的值 //Query.Not("name");//元素條件語句 //Query.NotIn("name", "a", 2);//返回與數組中所有條件都不匹配的文檔 //Query.Where(BsonJavaScript);//執行JavaScript //Query.Matches("Title", str);//模糊查詢 相當於sql中like -- str可包含正則表達式 var keyword = "abc"; Query.Matches("Title", new BsonRegularExpression("/.*" + keyword + ".*/"));//模糊Like語法 //通過正則表達式 1開頭 第二位數0~9且只能一位數,也包含20 var queryName = Query.Matches("Name", new BsonRegularExpression("Donma1([0-9]{1,1})$|20")); //查找年齡 >=10 且<=20 var queryAge = Query.And(Query.GTE("Age", 10), Query.LTE("Age", 20)); var entityQuery = Query<UserInfo>.EQ(e => e.Name, "wuhuacong"); var entityQuery2 = Query<UserInfo>.EQ(e => e.Id, "4B414D000000011613CD"); }
新版本的條件查詢,則丟棄了Query這個對象,提供了FilterDefinition<T> 對象的處理,估計是這個可以處理的更好吧,同時新介面全部支持非同步的處理操作了。
如插入記錄的非同步操作代碼如下所示。
/// <summary> /// 插入指定對象到資料庫中 /// </summary> /// <param name="t">指定的對象</param> public virtual async Task InsertAsync(T t) { ArgumentValidation.CheckForNullReference(t, "傳入的對象t為空"); IMongoCollection<T> collection = GetCollection(); await collection.InsertOneAsync(t); }
好了,基於篇幅的原因,把後面介紹的C#開發留到下一篇進行介紹,希望本篇文章對大家瞭解mongodb資料庫,以及如何在C#上面使用該資料庫提供了一個簡要的指引,希望大家多多支持。