一、MongoDB 簡介
1. 百科
MongoDB是一個基于分布式文件存儲的數(shù)據(jù)庫。由C++語言編寫。旨在為WEB應(yīng)用提供可擴展的高性能數(shù)據(jù)存儲解決方案。
MongoDB是一個介于關(guān)系數(shù)據(jù)庫和非關(guān)系數(shù)據(jù)庫之間的產(chǎn)品,是非關(guān)系數(shù)據(jù)庫當中功能最豐富,最像關(guān)系數(shù)據(jù)庫的。它支持的數(shù)據(jù)結(jié)構(gòu)非常松散,是類似json的bson格式,因此可以存儲比較復(fù)雜的數(shù)據(jù)類型。Mongo最大的特點是它支持的查詢語言非常強大,其語法有點類似于面向?qū)ο蟮牟樵冋Z言,幾乎可以實現(xiàn)類似關(guān)系數(shù)據(jù)庫單表查詢的絕大部分功能,而且還支持對數(shù)據(jù)建立索引。
2. 與MySQL相比:
- 數(shù)據(jù)模型,MongoDB是面向文檔的數(shù)據(jù)庫,MySQL是關(guān)系型數(shù)據(jù)庫。
- 查詢語言,MongoDB不支持join,MySQL支持join。
- 擴展性和性能,MongoDB使用可水平擴展的架構(gòu),MySQL使用垂直擴展的架構(gòu)。
- 可靠性,MongoDB目前只支持單文檔事務(wù),MySQL支持復(fù)雜事務(wù)操作。
3. 與Redis相比:
- 數(shù)據(jù)存儲方式:MongoDB使用文檔存儲方式,Redis使用鍵值對存儲方式。
- 數(shù)據(jù)類型:MongoDB可以存儲各種類型的數(shù)據(jù),包括文本、數(shù)字、日期、數(shù)組、嵌套文檔等等,Redis主要用于存儲簡單的數(shù)據(jù)類型,如字符串、哈希表、列表、集合等等。
- 存儲方式:MongoDB使用磁盤存儲數(shù)據(jù),Redis則是將數(shù)據(jù)存儲在內(nèi)存中。因此,MongoDB可以存儲更大量級的數(shù)據(jù),而Redis則更適合處理較小、經(jīng)常被訪問的數(shù)據(jù)。
- 數(shù)據(jù)訪問:MongoDB提供強大的查詢檢索功能,而Redis僅支持基本的查詢操作。
- 擴展性:MongoDB可以水平擴展,支持分布式架構(gòu),而Redis主要是通過主從復(fù)制的方式實現(xiàn)高可用性和可擴展性。
4. 適合的場景:
- 數(shù)據(jù)量大。
- 讀寫操作頻繁。
- 數(shù)據(jù)價值較低,對事務(wù)要求不高。
二、MongoDB 創(chuàng)建/刪除數(shù)據(jù)庫
db.cloneDatabase("127.0.0.1"); // 從指定主機上克隆數(shù)據(jù)庫
use database;
db.database.insertOne({"name":"B"});
db.dropDatabase();
三、MongoDB 創(chuàng)建/刪除集合
db.createCollection("database");
db.database.drop();
四、插入/刪除文檔
db.collections.insert(obj)
db.collections.insertOne( obj, < optional params > ):返回插入主鍵
db.collections.insertMany( [objects], < optional params > ):批量插入,返回插入的主鍵
db.users.insertMany([
{
"name": "Johs",
"age": 10,
"email": "johs@example.com"
},
{
"name": "John",
"age": 30,
"email": "john@example.com"
},
{
"name": "Jane",
"age": 25,
"email": "jane@example.com"
},
{
"name": "Bob",
"age": 40,
"email": "bob@example.com"
}
])

在創(chuàng)建集合期間,MongoDB 在 _id 字段上創(chuàng)建唯一索引,該索引可防止客戶端插入兩個具有相同的文檔
db.collections.save(obj):主鍵不存在插入,存在則替換
db.collections.drop():刪除集合
db.collections.remove({"字段":"值"}):刪除滿足條件的所有數(shù)據(jù)
db.collections.remove({"字段":"值"},true):刪除滿足條件的第一條數(shù)據(jù)
五、查詢文檔
db.collections.find(<條件>):查找所有文檔,相當于 select * from user
條件字段如果是字符串,表示包含;如果是數(shù)組,表示精確匹配;$all:匹配多個元素;$size:元素個數(shù);$slice:截??;$elemMatch:數(shù)組中是否有一個元素同時滿足所有條件;$type:字段類型;$exists:字段是否存在;null:字段值是否為null或沒有該字段
db.collection.findOne()
-
比較查詢:$lt 、$lte 、$gt 、$gte 、$ne
db.users.find({"name" : {"$ne" : "John"}}); db.users.find({"age" : {"$gte":20,"$lte": 30}}); -
范圍查詢:$in、$nin
db.users.find({"age" : {"$in" : [10, 30]}});// in不是between,是 = and = -
$where:通過js函數(shù)自定義查詢條件,此時不走索引
db.users.find({"$where" : "function() { return this.age == 10; }"}); -
正則表達式和數(shù)組
db.users.find({"name" : {"$regex" : "Ja" }}); db.users.find({"name" : {"$regex" : /Ja/ }}); db.users.find({"name" : /Ja/ }); -
條件查詢:$and、$or
db.users.find({"$or":[{"name":"Bob"},{"age":30}]}); -
特殊函數(shù):distinct、count、limit、skip、sort(正數(shù)升序)
db.users.distinct(); db.users.countDocuments(); db.users.find().sort({"age":-1}).skip(1).limit(3);
聚合框架aggregate
$match篩選:相當于sql中的where
$project投射:查詢指定字段、對字段起別名、使用算術(shù)表達式 $add、$subtract、$multiply、$divide、$mod 處理字段、$substrCP截取、$concat拼接、$toUpper/Lower轉(zhuǎn)大小寫、日期表達式、$cmp比較非字符串類型、$strcasecmp比較字符串、$and/or/not、$cond 三位運算符、ifNull(bool, default_value)
-
$group 分組、$unwind拆分、$sort、$limit、$skip
示例: db.user.aggregate( {"$match": {"age": {"$gte" : 10} }}, {"$group": {"_id": "$class", "count": {"$sum": 1}}}, {"$sort": {"count": -1}}, {"$skip": 1}, {"$limit": 1} ) 上述聚合函數(shù)相當于: SELECT class, COUNT(*) AS count FROM user WHERE age >= 10 GROUP BY classORDER BY count DESC LIMIT 1 OFFSET 1;
六、MongoDB 更新文檔
db.collection.update(<query>,<update>,
?{
??upsert: <boolean>,
??multi: <boolean>,
??writeConcern: <document>
?}
)
- query : update的查詢條件,類似sql update查詢內(nèi)where后面的。
- update : update的對象和一些更新的操作符(如
inc...)等,也可以理解為sql update查詢內(nèi)set后面的
- upsert : 可選,這個參數(shù)的意思是,如果不存在update的記錄,是否插入objNew,true為插入,默認是false,不插入。
- multi : 可選,mongodb 默認是false,只更新找到的第一條記錄,如果這個參數(shù)為true,就把按條件查出來多條記錄全部更新。
- writeConcern :可選,拋出異常的級別。
- 默認整體替換掉滿足條件的文檔而不是單獨的修改指定字段的值
db.collection.update(query, update, insertOrUpdate)
若insertOrUpdate為true,則更新策略變?yōu)椴淮嬖趧t添加,存在則更新
db.collection.update(query, update, insertOrUpdate, multiUpdate) 批量更新
$set:字段存在則修改,不存在則添加。對應(yīng)update中整個文檔進行替換
$unset:刪除字段。
$inc:字段遞增/減
$push:字段尾部添加元素,若字段不存在則創(chuàng)建數(shù)組
-
$push + $each:添加多個元素
db.user.update({"_id": 1}, {"$push": {"hobby": {"$each": ["money", "xiaojiejie"]}}}) $addToSet:向數(shù)組尾部添加不存在的元素,如果存在則不添加
$pop:彈出數(shù)組的頭部元素或尾部元素: -1:頭部,1:尾部。
$pull:刪除數(shù)組中的指定的值。
db.collection.findAndModify({ “query”: xxx, “update”: xxx,upsert):查找滿足添加的值并返回,并且修改滿足條件的第一條文檔
七、MongoDB 索引
db.collection.createIndex(keys, options); //創(chuàng)建索引
如果查詢時發(fā)現(xiàn)沒有使用到索引,可以使用hint函數(shù)強制使用索引查詢
1. 索引類型
- 唯一索引 unique;
- 稀疏索引 sparse:包含索引必須唯一,不包含不用校驗,對應(yīng)唯一索引校驗null
- 復(fù)合索引
- TTL 索引: 設(shè)置文檔的緩存時間,時間到了會自動刪除掉
- 全文索引 text:便于大文本查詢(如概要、文章等長文本)
- 二維平面索引:便于2d平面查詢;
- 地理空間索引:便于地理查詢
2. 索引選項
- 復(fù)合索引 name:"", 自定義索引的名稱,不配置系統(tǒng)會有默認的索引名。
- 復(fù)合索引 background: true, 默認是前臺模式, 創(chuàng)建索引是一件即費事又耗費資源的事情,創(chuàng)建索引是在前臺模式或者后臺模式下創(chuàng)建,在前臺模式下創(chuàng)建非常快,但是當有讀寫請求時會堵塞,在后臺模式下當有讀寫請求時并不堵塞,但是創(chuàng)建索引就會暫時暫停,后臺模式要比前臺模式慢的多。
- 復(fù)合索引 unique/sparse: true,索引
- 復(fù)合索引 dropDups: true,是否強制刪除其他重復(fù)的文檔,默認不刪除,當索引鍵值重復(fù)時創(chuàng)建失敗。
3. 對于索引的使用效率
- 復(fù)合索引索引鍵基數(shù)越大,效率越高。一些特殊的操作符不能使用索引,一般取反的操作符索引利用率都比較低。
- 復(fù)合索引如果能使用操作符盡量不要使用作符,因為or是執(zhí)行兩次查詢操作,然后將結(jié)果合并起來,類似于union all,能使用in(單次查詢)就不要使用or操作符。
八、MongoDB ObjectId
MongoDB中存儲的文檔必須有一個"_id"鍵。這個鍵的值可以是任何類型的,默認是個ObjectId對象。在一個集合里面,每個文檔都有唯一的"_id"值,來確保集合里面每個文檔都能被唯一標識。
MongoDB采用ObjectId,而不是其他比較常規(guī)的做法(比如自動增加的主鍵)的主要原因,因為在多個 服務(wù)器上同步自動增加主鍵值既費力還費時。
ObjectId 是一個12字節(jié) BSON 類型數(shù)據(jù),有以下格式:
- 前4個字節(jié)表示時間戳
- 接下來的3個字節(jié)是機器標識碼
- 緊接的兩個字節(jié)由進程id組成(PID)
- 最后三個字節(jié)是隨機數(shù)。
ObjectId();
ObjectId("64d34d4fe31e1a71dd3e5355").getTimestamp(); // getTimestamp 函數(shù)可以獲取文檔的創(chuàng)建時間
九、MongoDB 復(fù)制
MongoDB復(fù)制是將數(shù)據(jù)同步在多個服務(wù)器的過程。復(fù)制提供了數(shù)據(jù)的冗余備份,并在多個服務(wù)器上存儲數(shù)據(jù)副本,提高了數(shù)據(jù)的可用性, 并可以保證數(shù)據(jù)的安全性。復(fù)制還允許您從硬件故障和服務(wù)中斷中恢復(fù)數(shù)據(jù)。

mongodb的復(fù)制至少需要兩個節(jié)點。其中一個是主節(jié)點,負責處理客戶端請求,其余的都是從節(jié)點,負責復(fù)制主節(jié)點上的數(shù)據(jù)。
主節(jié)點記錄在其上的所有操作oplog,從節(jié)點定期輪詢主節(jié)點獲取這些操作,然后對自己的數(shù)據(jù)副本執(zhí)行這些操作,從而保證從節(jié)點的數(shù)據(jù)與主節(jié)點一致。
1. 添加副本集
replSet 選項
mongod --port "PORT" --dbpath "YOUR_DB_DATA_PATH" --replSet "REPLICA_SET_INSTANCE_NAME"
實例:
mongod --port 27017 --dbpath "D:\set up\mongodb\data" --replSet rs0
以上實例會啟動一個名為rs0的MongoDB實例,其端口號為27017。
啟動后打開命令提示框并連接上mongoDB服務(wù)。
在Mongo客戶端使用命令rs.initiate()來啟動一個新的副本集,使用rs.conf()來查看副本集的配置,使用 rs.status() 查看副本集狀態(tài)。
rs.add 命令
rs.add(HOST_NAME:PORT)
實例:
假設(shè)你已經(jīng)啟動了一個名為mongod1.net,端口號為27017的Mongo服務(wù)。 在客戶端命令窗口使用rs.add() 命令將其添加到副本集中,命令如下所示:
rs.add("mongod1.net:27017")
MongoDB中你只能通過主節(jié)點將Mongo服務(wù)添加到副本集中,判斷當前運行的Mongo服務(wù)是否為主節(jié)點可以使用命令db.isMaster() 。
MongoDB的副本集與我們常見的主從有所不同,主從在主機宕機后所有服務(wù)將停止,而副本集在主機宕機后,副本會接管主節(jié)點成為主節(jié)點,不會出現(xiàn)宕機的情況。
十、MongoDB 分片
在Mongodb里面存在另一種集群,就是分片技術(shù),可以滿足MongoDB數(shù)據(jù)量大量增長的需求。
當MongoDB存儲海量的數(shù)據(jù)時,一臺機器可能不足以存儲數(shù)據(jù),也可能不足以提供可接受的讀寫吞吐量。這時,我們就可以通過在多臺機器上分割數(shù)據(jù),使得數(shù)據(jù)庫系統(tǒng)能存儲和處理更多的數(shù)據(jù)。

上圖中主要有如下所述三個主要組件:
- Shard:
用于存儲實際的數(shù)據(jù)塊,實際生產(chǎn)環(huán)境中一個shard server角色可由幾臺機器組個一個replica set承擔,防止主機單點故障 - Config Server:
mongod實例,存儲了整個 ClusterMetadata,其中包括 chunk信息。 - Query Routers:
前端路由,客戶端由此接入,且讓整個集群看上去像單一數(shù)據(jù)庫,前端應(yīng)用可以透明使用。
1. 為什么使用分片
- 復(fù)制所有的寫入操作到主節(jié)點
- 延遲的敏感數(shù)據(jù)會在主節(jié)點查詢
- 單個副本集限制在12個節(jié)點
- 當請求量巨大時會出現(xiàn)內(nèi)存不足。
- 本地磁盤不足
- 垂直擴展價格昂貴