MongoDB 入門

本文主要記錄近期學(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-1mongod.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 #副本集的名稱

其它兩個分別修改dbPathpath幼衰、port即可靴跛。mongodb-2mongodb-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坦辟。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市章办,隨后出現(xiàn)的幾起案子锉走,更是在濱河造成了極大的恐慌,老刑警劉巖藕届,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件挪蹭,死亡現(xiàn)場離奇詭異,居然都是意外死亡休偶,警方通過查閱死者的電腦和手機梁厉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來踏兜,“玉大人词顾,你說我怎么就攤上這事〖钭保” “怎么了肉盹?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長疹尾。 經(jīng)常有香客問我上忍,道長,這世上最難降的妖魔是什么纳本? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任窍蓝,我火速辦了婚禮,結(jié)果婚禮上繁成,老公的妹妹穿的比我還像新娘吓笙。我一直安慰自己,他們只是感情好朴艰,可當(dāng)我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布观蓄。 她就那樣靜靜地躺著,像睡著了一般祠墅。 火紅的嫁衣襯著肌膚如雪侮穿。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天毁嗦,我揣著相機與錄音亲茅,去河邊找鬼。 笑死狗准,一個胖子當(dāng)著我的面吹牛克锣,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播腔长,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼袭祟,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了捞附?” 一聲冷哼從身側(cè)響起巾乳,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎鸟召,沒想到半個月后胆绊,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡欧募,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年压状,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片跟继。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡种冬,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出还栓,到底是詐尸還是另有隱情碌廓,我是刑警寧澤,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布剩盒,位于F島的核電站谷婆,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏辽聊。R本人自食惡果不足惜纪挎,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望跟匆。 院中可真熱鬧异袄,春花似錦、人聲如沸玛臂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至讽营,卻和暖如春虎忌,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背橱鹏。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工膜蠢, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人莉兰。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓挑围,卻偏偏與公主長得像,于是被迫代替她去往敵國和親糖荒。 傳聞我的和親對象是個殘疾皇子杉辙,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 一、MongoDB數(shù)據(jù)庫的定義: MongoDB是一個基于分布式文件存儲的數(shù)據(jù)庫捶朵。由C++語言編寫奏瞬。旨在為WEB應(yīng)...
    田小田txt閱讀 436評論 0 0
  • 慕課網(wǎng)學(xué)習(xí)筆記。學(xué)習(xí)視頻地址:https://www.imooc.com/learn/295教程版本為2.x泉孩,部分...
    edwin1993閱讀 551評論 0 1
  • 索引 索引能夠使得MongoDB更高效得執(zhí)行查詢硼端。如果沒有索引,MongoDB必須執(zhí)行集合掃描寓搬,即掃描集合中的每個...
    JervieQin閱讀 269評論 0 0
  • 進(jìn)行CRUD操作之前有必要了解mongoDB的數(shù)據(jù)模型珍昨。 MongoDB增刪改查 MongoDB 是一個基于分布式...
    JervieQin閱讀 221評論 0 1
  • 1 MongoDB中的基本概念及原理 1.1 MongoDB介紹 官網(wǎng)地址:https://www.mongodb...
    MiniSoulBigBang閱讀 521評論 0 0