本文主要記錄近期學(xué)習(xí) MongoDB 的一些內(nèi)容挑社,主要參考了官方文檔 https://docs.mongodb.com/v4.4/快毛,MongoDB 的官方文檔寫的還是挺不錯的驹愚,很詳細(xì)国夜,值得一看。
本文包含以下幾部分內(nèi)容:
- 認(rèn)識 MongoD
- 文檔的 CRUD 操作
- 聚合操作
- 索引
- 事務(wù)
- 副本集
下邊 MongoDB 的相關(guān)操作都是在 Mongo Shell 中執(zhí)行的佛吓,這種方式不需要考慮語言環(huán)境的問題蛇更,具有一定的通用性,也足以應(yīng)對一些不是特別復(fù)雜的操作筋讨,作為 MongoDB 入門也足夠了埃叭。
一、認(rèn)識 MongoD
MongoDB 屬于 NoSQL 類型數(shù)據(jù)庫的一種悉罕,它具有高性能赤屋、高可用、易擴展等特點壁袄。
MongoDB 是一個文檔數(shù)據(jù)庫类早,它以 Binary JSON(BSON)
的數(shù)據(jù)結(jié)構(gòu)來存儲文檔格式的數(shù)據(jù),簡單的說一條文檔數(shù)據(jù)就類似于 JSON 對象嗜逻,即鍵值對的形式涩僻,借用官方的以一張圖來認(rèn)識下文檔數(shù)據(jù):
其中 field 是字符串類型,但是 value 支持的數(shù)據(jù)類型相比傳統(tǒng)的 JSON 更加豐富栈顷,后邊我們細(xì)聊逆日。
為了直觀的認(rèn)識一下 MongoDB,我們來對比常見的關(guān)系型數(shù)據(jù)庫 MySQL萄凤,兩者還是有很多相似的地方:
MySQL | MongoDB |
---|---|
Database(數(shù)據(jù)庫) | Database(數(shù)據(jù)庫) |
Table(表) | Collection(集合) |
Row(行) | Document(文檔) |
Column(列) | Field(字段) |
Primary Key(主鍵) | Primary Key(主鍵) |
Index(索引) | Index(索引) |
Transactions(事務(wù)) | Transactions(事務(wù)) |
1.1室抽、一些特點
每一種數(shù)據(jù)庫都有自身相對適用的場景,如果我們的需求大致符合以下的場景靡努,就可以考慮使用 MongoDB坪圾,這樣可用更低的成本來解決問題:
- 需要支持事務(wù)晓折,從 MongoDB4.0 版本開始已經(jīng)逐步完善了對事務(wù)的支持,開始支持多文檔事務(wù)神年,通過分布式事務(wù)已维,事務(wù)可以跨多個操作、集合已日、數(shù)據(jù)庫、文檔和分片使用栅屏。
- 不需要復(fù)雜的多表關(guān)聯(lián)查詢(join)飘千,在 MongoDB 中使用 Aggregation Pipeline 中的 $lookup 操作符可以簡單的實現(xiàn) left outer join。
- 對文本以及地理位置數(shù)據(jù)的存儲栈雳、查詢有較強烈的需求护奈。當(dāng)然特別專業(yè)的文本檢索還是需要用搜素引擎的,比如 Elasticsearch
- 數(shù)據(jù)模型可能有比較頻繁的變更
- 有高并發(fā)的數(shù)據(jù)讀寫需求
- 需要存儲大規(guī)模的數(shù)據(jù)
- 對數(shù)據(jù)庫的擴展性以及可用性有較高的需求
1.2哥纫、常用數(shù)據(jù)類型
也就是 BSON 中支持的數(shù)據(jù)類型:https://docs.mongodb.com/manual/reference/bson-types/
類型 | 說明 |
---|---|
ObjectId | 是一個 12 字節(jié)的 16 進(jìn)制數(shù)霉旗,每個字節(jié)占兩位,總長度為 24 位蛀骇。具體是由 4 字節(jié)的時間戳(以 Unix 紀(jì)元以來的秒數(shù)為單位)厌秒、5 字節(jié)的根據(jù)機器和進(jìn)程生成的唯一隨機值、3 字節(jié)的遞增計數(shù)器值(使用前邊的隨機值作為起始值)組成擅憔。每條文檔數(shù)據(jù)都有一個默認(rèn)字段_id 作為主鍵鸵闪,如果沒有指定值,MongoDB 會指定一個 ObjectId 類型的默認(rèn)值 |
String | 必須是 UTF-8 編碼格式的字符串 |
Double | 浮點數(shù)暑诸,MongoDB 中沒有 Float 類型 |
Decimal128 | 高精度浮點數(shù)蚌讼,可用于存儲價格等數(shù)據(jù) |
Integer | 分為 32 位(NumberInt)和 64(NumberLong)位兩種整數(shù) |
Boolean | true 或者 false |
Object | 對象,可用于嵌入另外一個文檔 |
Null | 存儲空值 |
Array | 用于存儲數(shù)組或者列表 |
Date | 日期个榕,是一個 64 位整數(shù)篡石,從 1970.1.1(Unix 紀(jì)元)到現(xiàn)在的毫秒數(shù), 也稱為 UTC 日期時間西采,推薦使用 Date 類型處理日期 |
Timestamp | 時間戳凰萨,64位,前 32 位是從 1970.1.1(Unix 紀(jì)元)到現(xiàn)在的秒數(shù)苛让,后 32 位是給定秒內(nèi)操作的遞增序數(shù)沟蔑,無法與 Date 類型關(guān)聯(lián),主要供內(nèi)部 MongoDB 使用狱杰,在單個 mongod 實例中瘦材,時間戳值始終是唯一的 |
Regular Expression | 存儲正則表達(dá)式 |
Code | 存儲 JavaScript 代碼 |
Binary Data | 存儲二進(jìn)制數(shù)據(jù) |
1.3、數(shù)據(jù)庫仿畸、集合食棕、文檔
https://docs.mongodb.com/v4.4/core/databases-and-collections/
MongoDB 將數(shù)據(jù)記錄以文檔(Document
)的形式保存集合(Collection
)中朗和、集合又需要存儲到對應(yīng)的數(shù)據(jù)庫(Database
)中,所以掌握它們的一些常用操作簿晓。
MongoDB 中對數(shù)據(jù)庫眶拉、集合、文檔 field 等的命名規(guī)范以及長度都有一定的限制憔儿,可以參考官方文檔查看 忆植。
MongoDB 的安裝可以參考文章最后一部分,起動安裝的 MongoDB 節(jié)點谒臼,然后連接到主節(jié)點:
mongo -u <user> -p <pass> --host <host> --port <port>
除了mongo
之外朝刊,其它參數(shù)都是可選的,host
蜈缤、port
默認(rèn)是本機的27017
端口拾氓。
1.3.1、創(chuàng)建底哥、切換數(shù)據(jù)庫
use mydb
通過use
命令可以切換到指定的已存在的數(shù)據(jù)庫咙鞍,如果數(shù)據(jù)庫不存在,則會在第一次給數(shù)據(jù)庫創(chuàng)建集合時創(chuàng)建對應(yīng)的數(shù)據(jù)庫趾徽。
1.3.2续滋、查看所有的數(shù)據(jù)庫
show dbs
show databases
上邊兩個命令都可以查看權(quán)限內(nèi)的所有數(shù)據(jù)庫。
1.3.3附较、查看當(dāng)前所在的數(shù)據(jù)庫
db
1.3.4吃粒、刪除數(shù)據(jù)庫
db.dropDatabase()
切換到指定數(shù)據(jù)庫后,才可以刪除數(shù)據(jù)庫拒课。
1.3.5徐勃、查看數(shù)據(jù)庫中的所有集合
show collections
先切換到指定數(shù)據(jù),才可以查看其中的集合早像。
1.3.6僻肖、創(chuàng)建集合
db.createCollection("book")
這種屬于指定集合名稱顯式的創(chuàng)建方式,需要先切換到指定數(shù)據(jù)庫卢鹦。
除了顯式的創(chuàng)建集合臀脏,還在隱式創(chuàng)建,如果集合不存在冀自,當(dāng)?shù)谝煌现斜4鏀?shù)據(jù)時就會自動創(chuàng)建對應(yīng)的集合揉稚,比如:
db.book.inserOne({})
1.3.7、刪除集合
db.book.drop()
切換到指定數(shù)據(jù)庫后熬粗,就可以根據(jù)名稱來刪除集合搀玖。
二、文檔的 CRUD 操作
https://docs.mongodb.com/v4.4/crud/
2.1驻呐、插入
2.1.1灌诅、insertOne()
insertOne()
是給指定集合中插入單條文檔芳来,如果文檔沒有指定_id
字段,則 MongoDB 會添加一個值為 ObjectId _id 字段到文檔中猜拾,注意 _id 字段為默認(rèn)主鍵即舌。
db.book.insertOne({
"author": "劉慈欣",
"commentCount": 1038006,
"img": "http://img14.360buyimg.com/n1/s200x200_jfs/t1705/189/702227414/177982/cc8c12f0/55dab54dN5271c377.jpg",
"name": "中國科幻基石叢書:三體(套裝1-3冊)",
"price": 83.7,
"publisher": "重慶出版社",
"shop": "科幻世界京東自營店",
"skuId": "11757834"
})
插入數(shù)據(jù)時如果不指定字段的類型,則 MongoDB 會使用自推斷出數(shù)據(jù)類型來存儲數(shù)據(jù)挎袜,但這可能不是你需要的理想類型顽聂。
2.1.2、insertMany()
如果要一次插入多條文檔盯仪,可以使用insertMany()
芜飘,并以數(shù)組形式傳入文檔,_id
字段的處理規(guī)則和上邊一致磨总。
db.book.insertMany([
{
"author": "紫金陳",
"commentCount": 36164,
"img": "http://img14.360buyimg.com/n1/s200x200_jfs/t1/130390/12/10177/391461/5f63788cE9b818814/fa5465bd1280778e.jpg",
"name": "長夜難明(紫金陳社會派懸疑推理小說必讀 迷霧劇場廖凡白宇出演的網(wǎng)劇《沉默的真相》原著小說)",
"price": 42,
"publisher": "云南人民出版社",
"shop": "浦睿文化京東自營店",
"skuId": "12081064"
},
{
"author": "魯迅",
"commentCount": 25567,
"img": "http://img11.360buyimg.com/n1/s200x200_g15/M0A/0F/18/rBEhWFJeUd4IAAAAAAbFh_6HtZsAAENlQKKQEoABsWf482.jpg",
"name": "吶喊",
"price": 10.6,
"publisher": "譯林出版社",
"shop": "鳳凰壹力京東自營店",
"skuId": "11338556"
}
])
2.1.3、insert()
insert()
既可以用來給集合中插入單條文檔笼沥,也可以通過數(shù)組參數(shù)插入多條文檔蚪燕。
db.book.insert({})
db.book.insert([{}, {}])
2.2、查詢
查詢主要是通過find(query, projection)
方法來實現(xiàn)的奔浅,find()
會返回符合條件的全部文檔馆纳,但需要注意在 mongo shell 中并不是一次性返回集合中的全部文檔。
比如下邊的查詢(沒有查詢條件{}
可以省略):
db.book.find({})
該查詢實際會返回一個游標(biāo)(cursor)汹桦,但我們沒有通過定義var
變量接收游標(biāo)鲁驶,所以游標(biāo)會自動迭代最多 20 次,所以該查詢最多返回 20 條文檔舞骆。
如果我們通過變量來接收返回的游標(biāo)钥弯,并手動調(diào)用游標(biāo)變量,同樣游標(biāo)會自動迭代 20 次督禽,并返回對應(yīng)的文檔:
> var c = db.book.find({})
> c
> c
當(dāng)然也可以手動迭代脆霎,逐條返回數(shù)據(jù):
> var c = db.book.find({})
> while (c.hasNext()) {
printjson(c.next());
}
這個默認(rèn)的 20 次可以通過如下方式修改:
DBQuery.shellBatchSize = 30
MongoDB 中提供了許多查詢相關(guān)的操作符,來滿足不同的場景狈惫,
2.2.1睛蛛、比較運算符
比較運算符有$eq
、$gt
胧谈、$gte
忆肾、$lt
、$lte
菱肖、$ne
客冈、$in
、$nin
蔑滓,我們來看幾個例子
比如查詢author
的是紫金陳
的文檔:
db.book.find({author: {$eq: "紫金陳"}})
也可以使用下邊的簡化寫法:
db.book.find({author: "紫金陳"})
但需要注意郊酒,如果使用$eq
時查詢條件的值是正則表達(dá)式遇绞,那么只會匹配字段值也是正則表達(dá)式的文檔,比如下邊的模糊查詢是沒有結(jié)果的:
db.book.find({author: {$eq: /金/}})
需要使用如下的查詢方式才能查詢到author
中包含金
的文檔:
db.book.find({author: /金/})
db.book.find({author: {$regex: /金/}})
查詢price
小于100
的書:
db.book.find({price: {$lt: 100}})
查詢author
是紫金陳
或者魯迅
的文檔:
db.book.find({author: {$in: ["紫金陳", "魯迅"]}})
這個功能也可以使用邏輯運算符$or
實現(xiàn)燎窘,但這種場景還是建議使用$in
詳細(xì)內(nèi)容可參考文檔:https://docs.mongodb.com/v4.4/reference/operator/query-comparison/
2.2.2摹闽、邏輯運算符
邏輯運算符有$and
、$or
褐健、$not
付鹿、$nor
。
比如查詢name
是吶喊
并且price
小于等于50
的文檔蚜迅,可以使用$and
:
db.book.find({$and: [{name: "吶喊"}, {price: {$lte: 50}}]})
還有一種等價簡單的寫法舵匾,提供了隱式的$and
操作:
db.book.find({name: "吶喊", price: {$lte: 50}})
查詢price
小于 100 或者commentCount
大于100000的文檔,可以使用$or
:
db.book.find($or: [{price: {$lt: 100}}, {commentCount: {$gt: 100000}}])
$not
表示邏輯非操作谁不,即對查詢條件取反坐梯,同時也會查詢不包含對應(yīng)字段的文檔,如下查詢語句:
db.book.find({price: {$not: {$lt: 100}}})
會查詢price
大于 100 或者不包含price
的文檔刹帕。
$nor
會返回不匹配所有指定字段的查詢條件的文檔吵血,比如:
db.book.find($nor: [{author: "魯迅"}, {commentCount: {$gt: 100000}}])
會查詢author
不是魯迅
、commentCount
不大于 100000偷溺、以及不包含對應(yīng)字段的文檔蹋辅。
2.2.3、文檔字段與查詢
2.2.3.1
find()
方法默認(rèn)會返回文檔的全部字段挫掏,我們可以通過它的第二個參數(shù)來指定只返回哪些字段和不返回那些字段侦另。
db.book.find({}, {name: 1})
理論上我們希望返回的文檔只有name
字段,但其實也包含_id
字段尉共,因為_id
字段比較特殊我們可以手動指定不返回:
db.book.find({}, {name: 1, _id: 0}
所以1
表示只會返回對應(yīng)的字段褒傅,其它字段不返回(_id
需要單獨指定);0
表示不會返回對應(yīng)的字段爸邢,其它字段不受影響樊卓。
2.2.3.2
有時我們需要查詢存在某個字段的文檔,可以使用$exists
:
db.book.find({img: {$exists: true}})
上邊會查詢有img
字段的文檔杠河。
2.2.3.3
還可以按照字段類型還查詢文檔數(shù)據(jù)碌尔,$type
可以用來查詢字段值為指定 BSON 類型的文檔:
db.book.find({commentCount: {$type: "long"}})
也可以用數(shù)組表示多個類型:
db.book.find({commentCount: {$type: ["long", "string"]}})
2.2.3.4
要獲取一個字段的類型,可以使用typeof
:
typeof db.book.name
要判斷一個字段是否為指定類型券敌,可以使用instanceof
:
db.book.commentCount instanceof NumberInt
2.2.3.5
mongo 中關(guān)于null
值的查詢比較特殊唾戚,如下查詢會返回img
字段值為null
以及不存在該字段的文檔:
db.book.find(img: null)
2.2.4、排序
find()
方法查詢到的結(jié)果默認(rèn)按_id
升序排列待诅,我們也可以使用sort()
方法按照指定字段來排序叹坦。
db.book.find().sort({price: 1})
price: 1
表示查詢結(jié)果按照price
升序排列,1
表示升序卑雁、-1
表示降序募书。
也可以同時用多個字段排序:
db.book.find().sort({price: 1, commentCount: -1})
2.2.5绪囱、分頁
分頁查詢一般通過limit()
、skip()
兩個方法結(jié)合實現(xiàn):
- limit: 控制每次查詢返回的文檔數(shù)量
- skip: 從查詢結(jié)果中跳過指定數(shù)量的文檔再開始返回
db.book.find().sort({price: 1}).limit(10).skip(20)
從查詢結(jié)果中跳過20條數(shù)據(jù)后取10條數(shù)據(jù)返回莹捡,即第三次分頁查詢鬼吵。
這種方式用起來簡單,但是當(dāng)數(shù)據(jù)量比較大的時候篮赢,比如幾十萬到一百萬左右齿椅,越往后分頁查詢就越慢,有比較明顯的性能問題启泣。所以不太適合大量數(shù)據(jù)的全量分頁查詢涣脚,只查詢少量數(shù)據(jù)還是可以的。
當(dāng)然在實際中也是有大量數(shù)據(jù)的全量分頁查詢需求的寥茫,此時我們可以指定一個排序字段遣蚀,保證字段值按一定的規(guī)律增長即可。這樣每次查詢時需要攜帶一個排序字段的基準(zhǔn)值纱耻,查詢前先按照排序字段排序妙同,再查詢排序字段值大于或小于該基準(zhǔn)值的指定數(shù)據(jù)量數(shù)據(jù)。
2.3膝迎、更新
更新文檔的方法可以指定三個參數(shù):
-
filter
,文檔的過濾條件也就是要更新哪些文檔 -
update
胰耗,如何更新文檔 -
options
限次, 可選參數(shù),指定一些額外的屬性
具體的可以參考文檔 https://docs.mongodb.com/v4.4/reference/update-methods/
比如要更新匹配過濾條件的第一條文檔柴灯,可以使用updateOne()
:
db.book.updateOne({skuId: "11757834"}, {$set: {price: 66.6}, $currentDate: {lastModified: true}}, {upsert: true})
{skuId: "11757834"}
是過濾條件卖漫,$set
操作符用來更新指定字段的值。$currentDate
操作符用來將lastModified
字段的值更新為當(dāng)前時間(默認(rèn) Date)(如果該字段不存在則會自動創(chuàng)建)赠群。upsert
是可選屬性羊始,要更新的文檔不存在時決定是否插入新文檔。
updateMany()
和updateOne()
用法類似查描,但是會更新匹配條件的全部文檔突委,比如將指定作者的書調(diào)價:
db.book.updateMany({author: "魯迅"}, {$inc: {price: 2}})
還有一個update()
方法,可以通過指定第三個參數(shù)的multi
屬性來決定是更新單個文檔還是多個文檔冬三。
除了上邊用的$set
匀油、$currentDate
、$inc
這幾個更新操作符之外勾笆,其它的可以參考文檔 https://docs.mongodb.com/manual/reference/operator/update/
最后還有一個replaceOne()
方法敌蚜,可以用新文檔替換掉符合過濾條件的第一個文檔。
2.4窝爪、刪除
刪除操作相對簡單一些弛车,根據(jù)指定的過濾條件齐媒,可以一次最多刪除一條,也可以刪除全部纷跛。
deleteOne()
每次最多刪除一條文檔:
db.book.deleteOne({author: "魯迅"})
deleteMany()
則可以刪除全部符合條件的喻括。
還有一個remove()
方法,可以通過justOne
屬性指定一次刪除最多一條還是全部:
db.book.remove({author: "魯迅"}, {justOne: false})
三忽舟、聚合操作
除了一般的查詢操作双妨,MongoDB 提供的聚合查詢也是非常有用的,主要可以實現(xiàn)對文檔數(shù)據(jù)的統(tǒng)計叮阅、分析功能刁品。這里我們主要了解一下聚合管道
,至于Map-Reduce
就不討論了浩姥,它基本可以由聚合管道
代替挑随,同時可以獲得更高的性能以及可以用性。最后還有一些簡單的聚合操作方法
可以了解下勒叠,方便處理一下簡單的需求兜挨。
詳細(xì)的內(nèi)容可以直接參考官方文檔 Aggregation。
3.1眯分、聚合管道
聚合管道拌汇,類似于流水線處理,對輸入的文檔進(jìn)行一個或者多個階段的連續(xù)處理弊决,上一個處理階段的輸出作為下一個階段的輸入噪舀,直到所有階段全部完成后輸出最終的處理結(jié)果。
先看一個例子來感受一下這個過程飘诗,比如先過濾出commentCount
大于 100000 的文檔与倡、再按author
字段分組并統(tǒng)計出組內(nèi)總的文檔數(shù)bookCount
、然后按照bookCount
降序排列:
db.book.aggregate([
{$match: {commentCount: {$gt: 100000}}},
{$group: {_id: "$author", bookCount: {$sum: 1}}},
{$sort: {bookCount: -1}}
])
經(jīng)過上邊三個階段的處理昆稿,最終的結(jié)果如下:
在聚合管道中纺座,每一個階段(Stage)都需要指定一個操作符來做具體的事情,比如前邊的$match
溉潭、$group
净响、$sort
。目前官方提供了大約 30 個階段操作符喳瓣,具體的可以參考文檔 Aggregation Pipeline Stages别惦,每個操作符都有詳細(xì)的用法。
這里簡單介紹一下夫椭,知道每個操作符大概是做什么的掸掸,可以在那些場景使用:
-
$match
,從中的文檔數(shù)據(jù)中過濾出符合查詢條件的 -
$group
,對文檔按指定字段進(jìn)行分組扰付,字段的值相同的會被分為一組堤撵,同時可以對組內(nèi)數(shù)據(jù)進(jìn)行各種計算操作,最后為每組輸出一個文檔羽莺,分組也可以實現(xiàn)數(shù)據(jù)去重的效果 -
$bucket
实昨,功能和$group
類似也會對文檔分組,但是它可以指定分組字段的值的區(qū)間盐固,對應(yīng)區(qū)間的文檔會被分為一組 -
$sort
荒给,對文檔按指定字段排序,可以指定多個字段刁卜,-1
降序志电、1
升序 -
$count
,返回管道中當(dāng)前階段的文檔數(shù) -
$sortByCount
蛔趴,對輸入的文檔按指定字段的值進(jìn)行分組挑辆,然后計算每組的文檔數(shù),相當(dāng)于$group
+$sort
-
$skip
孝情,跳過指定個數(shù)的文檔鱼蝉,將剩余的傳遞到下一階段 -
$limit
,指定傳遞到下一階段的文檔數(shù) -
$project
箫荡,給文檔添加新字段或者刪除已有字段魁亦,即重構(gòu)文檔結(jié)構(gòu) -
$addFields
,給文檔中添加新字段羔挡,它還有一個別名$set
吉挣,和$project
的添加功能類似 -
$unset
,從文檔中刪除已有的字段婉弹,和$project
的刪除功能類似 -
$unwind
,對文檔中的數(shù)組類型字段進(jìn)行結(jié)構(gòu)操作终吼,即將數(shù)組值拆分成多個元素镀赌,每個元素值將會替換原數(shù)組的值并且和當(dāng)前文檔的其它字段組成一個新的文檔 -
$lookup
,主要的功能是兩個集合之間的關(guān)聯(lián)查詢际跪,類似于 MySQL 中的 left join -
$unionWith
商佛,可以將兩個集合的管道處理結(jié)果集組合成一個新的結(jié)果集(不會去重),然后輸入到管道中的下一階段 -
$out
姆打,必須出現(xiàn)在聚合管道的最后一個階段良姆,將前邊管道返回的文檔寫入指定的集合中
3.2、簡單的聚合操作
簡單的聚合操作主要是針對單個集合中文檔的操作蜡镶,功能都很簡單勺爱。
前幾個都是統(tǒng)計集合中文檔數(shù)的甲献。
3.2.1端壳、count()
db.book.count({author: "魯迅"})
使用count()
方法時盡量不要省略過濾條件痊剖,否則會使用元數(shù)據(jù)來計數(shù)韩玩,在分片集群上、非常正關(guān)機可能導(dǎo)致結(jié)果不準(zhǔn)確陆馁。
上邊的計數(shù)統(tǒng)計也等價于:
db.book.find({author: "魯迅"}).count()
除了設(shè)置過濾條件找颓,還支持可選的參數(shù),比如設(shè)置最多統(tǒng)計的文檔數(shù)叮贩,避免統(tǒng)計過多的數(shù)據(jù)做無用功:
db.book.count({author: "魯迅", commentCount: {$gt: 100000}}, {limit: 10000})
3.2.2击狮、estimatedDocumentCount()
db.book.estimatedDocumentCount()
這個不方法支持過濾條件,它直接使用元數(shù)據(jù)來計數(shù)益老,也存在上邊count()
方法的問題
3.2.3彪蓬、countDocuments()
db.book.countDocuments({author: "魯迅"})
這個方法支持過濾條件以及一些可以選的參數(shù)(比如limit
),它不會使用元數(shù)據(jù)統(tǒng)計文檔數(shù)量杨箭,而是通過文檔的聚合返回準(zhǔn)確的統(tǒng)計結(jié)果寞焙。而且沒有上邊兩個方法存在的問題。
3.2.4互婿、distinct()
在單個集合中根據(jù)指定的過濾條件查找指定字段的不同值捣郊,類似于去重,并且以數(shù)組形式返回字段的值慈参,比如
db.book.distinct("author", {commentCount: {$gt: 100000}})
四呛牲、索引
4.1、認(rèn)識索引
MongoDB 中的索引和 MySQL 中的類似驮配,MongoDB 中使用索引可以高效的進(jìn)行數(shù)據(jù)查詢娘扩,如果不使用索引,查詢過程中就必須掃描集合中的每個文檔壮锻,進(jìn)而找到匹配查詢條件的琐旁,有了索引了,則會從索引中找到匹配查詢條件的文檔猜绣。
MongoDB 中的索引是集合級別的灰殴,可以給文檔中任何字段(包括嵌套的子字段)創(chuàng)建索引,索引使用 B-tree 的數(shù)據(jù)結(jié)構(gòu)掰邢,它會存儲作為索引的字段的值牺陶,并且按照字段值升序或降序排列,來提高查詢的效率辣之。
借用官方的一張圖來看一下使用索引時的查詢過程:
4.2掰伸、創(chuàng)建索引
MongoDB 在創(chuàng)建集合時,默認(rèn)會在_id
字段上創(chuàng)建唯一索引怀估,索引名為_id_
并且該索引不能被刪除狮鸭,這樣也可以保證_id
字段值相同的文檔不會被重復(fù)插入到集合中合搅。
除了默認(rèn)的_id
字段上的索引,我們也可以使用createIndex()
方法給其它字段創(chuàng)建索引怕篷。
首先可以給單個字段創(chuàng)建索引历筝,比如:
db.book.createIndex({skuId: -1}, {name: "skuIdIndex"})
上邊給
author
字段創(chuàng)建了索引,-1
表示創(chuàng)建降序索引廊谓,即索引中的值按降序排列梳猪,1
則表示升序索引,對于單字段索引蒸痹,使用降序或升序都可以春弥,因為 MongoDB 可以從任意一個方向來遍歷索引。name
則用來指定索引的名稱叠荠,也可以直接使用默認(rèn)值匿沛,索引的默認(rèn)名稱由索引鍵
(指定的字段名)和索引鍵方向
(-1或1)使用下劃線拼接成,上邊例子的默認(rèn)索引名就是skuId_-1
榛鼎。
最后可以查看創(chuàng)建好的索引:
此時也可以看到 MongoDB 在_id
字段上創(chuàng)建的默認(rèn)索引逃呼。
除了上邊的單字段索引,還有復(fù)合索引者娱,使用復(fù)合索引可以同時在多個字段上創(chuàng)建索引抡笼,此時字段之間的順序很重要,這也決定了索引中數(shù)據(jù)排序的先后順序黄鳍。如下創(chuàng)建一個復(fù)合索引:
db.book.createIndex({auther: 1, name: 1})
這里沒有指定索引名稱推姻,而是使用默認(rèn)的,查看創(chuàng)建好的索引:
五框沟、事務(wù)
在 MongoDB4.0 版本之前藏古, 僅對單個文檔的操作是具有原子性的,即單文檔事務(wù)忍燥,這里因為 MongoDB 中文檔是可以嵌套子文檔的拧晕,這樣就解決了一般關(guān)系型數(shù)據(jù)庫需要多張表才能描述不同數(shù)據(jù)之間關(guān)系的問題,所以這樣也能在一定程度上保證數(shù)據(jù)的一致性梅垄。
但是對于同一集合或者不同集合甚至不同數(shù)據(jù)庫中的多個文檔的讀寫就不能保證原子性了厂捞,但這個需求顯然也是存在的,所以從 MongoDB4.0 開始支持多文檔事務(wù)了:
- 在 MongoDB4.0 版本中哎甲,支持副本集上的多文檔事務(wù)
- 在 MongoDB4.2 版本中,新增了分片集群上多文檔事務(wù)的支持饲嗽,同時也合并了副本集上多文檔事務(wù)的支持炭玫。從 MongoDB4.2 版本開始
多文檔事務(wù)
也稱為分布式事務(wù)
。
在大多數(shù)情況下貌虾,多文檔事務(wù)比單文檔事務(wù)有著更大的性能開銷吞加,所以盡可能的去優(yōu)化文檔的結(jié)構(gòu),使用單文檔事務(wù),而不是過分的依賴多文檔事務(wù)衔憨。
MongoDB 在單節(jié)點模式下是不支持事務(wù)的叶圃,所以要使用 MongoDB 的事務(wù)特性就需要搭建副本集(Replica Set
)或者分片集群(Sharded Cluster
)
關(guān)于事務(wù)這里簡單的介紹一下,更多內(nèi)容還請參考官方文檔 https://docs.mongodb.com/v4.4/core/transactions/
六践图、副本集
副本集是一組維護(hù)相同數(shù)據(jù)集的 Mongod 實例掺冠,具有自動的故障恢復(fù)、讀寫分離等特點码党,其中包含一個主節(jié)點(Primary
)德崭、一個或多個副本節(jié)點(Secondary
)、一個可選的仲裁節(jié)點(Arbiter
)揖盘。
- 主節(jié)點負(fù)責(zé)處理所有的寫操作眉厨,也可以處理查詢操作
- 副本節(jié)點會作從主節(jié)點同步數(shù)據(jù),不處理寫操作兽狭,但可以通過修改客戶端連接支持讀操作來減少主節(jié)點的壓力憾股,主節(jié)點掛掉時會參與主節(jié)點的選舉、投票
- 沖裁節(jié)點不存儲數(shù)據(jù)箕慧,不參與主節(jié)點的選舉服球,但是會參與投票,一般會在硬件成本有限的情況下使用仲裁節(jié)點來代替副本節(jié)點销钝,同時也保證了副本集的正常工作有咨。
關(guān)于副本集詳細(xì)的介紹可以參考 https://docs.mongodb.com/manual/replication/
搭建副本集可以采用 一個主節(jié)點+偶數(shù)個副本節(jié)點 或者 一個主節(jié)點+奇數(shù)個副本節(jié)點+一個仲裁節(jié)點 的方式,這里簡單介紹在 Winddows 環(huán)境下采用1主1副本1仲裁的組合模擬搭建副本集蒸健。
這里下載 MongoDB4.4.10 版本 zip 包座享,解壓3份,如下:
分別在3個根目錄下創(chuàng)建conf
似忧、data\db
渣叛、log
目錄,然后在conf
盯捌、log
分別創(chuàng)建mongod.conf
淳衙、mongod.log
文件。
下邊是mongodb-1
中mongod.conf
的內(nèi)容:
storage:
dbPath: D:\mongodb-4.4.10\mongodb-1\data\db # mongod實例的數(shù)據(jù)存儲目錄
journal:
enabled: true # 啟用持久性日志以確保數(shù)據(jù)文件保持有效和可恢復(fù)
systemLog:
destination: file
path: D:\mongodb-4.4.10\mongodb-1\log\mongod.log # 指定日志文件
logAppend: true # 重啟時將日志追加到現(xiàn)有之日文件末尾
net:
bindIp: localhost # 綁定ip饺著,默認(rèn)就是localhost
port: 27017 # 綁定的端口號箫攀,默認(rèn)就是27017
replication:
replSetName: myrs #副本集的名稱
其它兩個分別修改dbPath
、path
幼衰、port
即可靴跛。mongodb-2
、mongodb-3
的端口分別指定為27018
渡嚣、27019
配置完后梢睛,分別在bin
目下啟動3個節(jié)點:
mongod -f ../conf/mongod.conf
連接到mongodb-1
(也可以連接到其它節(jié)點肥印,可以在bin目錄下啟動也可以配置環(huán)境變量后在任意目錄啟動),進(jìn)行副本集初始化:
mongo --host localhost --port 27017
rs.initiate()
初始化成功后绝葡,mongodb-1
默認(rèn)會成為主節(jié)點深碱,向副本集中添加副本節(jié)點mongodb-2
:
rs.add("localhost:27018")
還需要添加仲裁節(jié)點mongodb-3
:
rs.addArb("localhost:27019")
連接到上邊添加的副本節(jié)點,使其支持讀操作:
mongo --host localhost --port 27018
rs.secondaryOk()
到這里基本的配置就完成了藏畅,向主節(jié)點添加數(shù)據(jù)后敷硅,副本節(jié)點會自動同步主節(jié)點的數(shù)據(jù),如果主節(jié)點掛掉則會從副本節(jié)點中重新選舉新的主節(jié)點墓赴,后期重新啟動副本集的話竞膳,也會選舉新的主節(jié)點。
本文內(nèi)容到這里就結(jié)束了诫硕,下一篇我們將學(xué)習(xí)如在 Springboot 中使用 MongoDB坦辟。