在花了不少時間研究學習了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']})
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#上面使用該數據庫提供了一個簡要的指引,希望大家多多支持。