MongoDB學(xué)習(xí)筆記(一)

  • MongoDB是一個(gè)開源、高性能、無模式的文檔型數(shù)據(jù)庫,是NoSQL產(chǎn)品中的一種。
  • MongoDB中記錄的是一個(gè)文檔,所謂文檔就是一種類似于JSON的格式叫BSON,它是一種由字段和值對組成的數(shù)據(jù)結(jié)構(gòu)。字段的數(shù)據(jù)類型是字符型,它的值除了使用基本的一些類型外,還可包括其他文檔、普通數(shù)組和文檔數(shù)組。
  • 應(yīng)用場景:MongoDB可應(yīng)對“三高”(高并發(fā)、高性能、高可用)需求。如:

①社交場景:用于存儲用戶個(gè)人信息;用戶發(fā)表的朋友圈信息;通過地理位置索引實(shí)現(xiàn)附近的人、地點(diǎn)等功能。
②游戲場景:用于存儲游戲用戶信息,用戶的裝備、積分等直接以內(nèi)嵌文檔的形式存儲,方便查詢、高效率存儲和訪問。
③物流場景:用于存儲訂單信息,訂單狀態(tài)在運(yùn)送過程中會不斷更新,以內(nèi)嵌數(shù)組的形式來存儲,一次查詢就能將該訂單所有的變更讀取出來。
④物聯(lián)網(wǎng)場景:用于存儲所有接入的智能設(shè)備信息,設(shè)備匯報(bào)的日志信息,并對這些信息進(jìn)行多維度的分析。
⑤視頻直播:用于存儲用戶信息、點(diǎn)贊互動信息等。

  • 以上這些應(yīng)用場景中,數(shù)據(jù)操作方面的共同特點(diǎn)是:①數(shù)據(jù)量大;②讀寫操作都很頻繁;③數(shù)據(jù)價(jià)值較低,對事務(wù)性要求不高。
SQL術(shù)語 MongoDB術(shù)語 解釋
database database 數(shù)據(jù)庫
table collection 數(shù)據(jù)庫表/集合
row document 數(shù)據(jù)記錄行/文檔
column field 數(shù)據(jù)字段/域
index index 索引
table joins - 表連接/MongoDB不支持
- 嵌入式文檔 MongoDB通過嵌入式文檔來替代多表連接
primary key primary key 主鍵/MongoDB自動將_id字段設(shè)置為主鍵
  • MongoDB的最小存儲單位是文檔(document)對象,對應(yīng)關(guān)系型數(shù)據(jù)庫的行(row)。數(shù)據(jù)在MongoDB中以BSON(Binary-JSON)文檔的格式存儲在磁盤上。
  • BSON和JSON一樣,支持內(nèi)嵌的文檔對象和數(shù)組對象,但是BSON有JSON沒有的一些數(shù)據(jù)類型,如DateBinData類型。
  • BSON采用了類似于 C 語言結(jié)構(gòu)體的名稱、對表示方法,支持內(nèi)嵌的文檔對象和數(shù)組對象,具有輕量性、可遍歷性、高效性的三個(gè)特點(diǎn),可以有效描述非結(jié)構(gòu)化數(shù)據(jù)和結(jié)構(gòu)化數(shù)據(jù)。這種格式的優(yōu)點(diǎn)是靈活性高,但它的缺點(diǎn)是空間利用率不高
  • BSON中,除了基本的JSON類型:stringinteger、booleandouble、nullarrayobject,MongoDB還使用了特殊的數(shù)據(jù)類型:date、object idbinary data、regular expressioncode。
  • BSON數(shù)據(jù)類型參考列表:
數(shù)據(jù)類型 描述 舉例
字符串 UTF-8字符串都可表示為字符串類型的數(shù)據(jù) {"x" : "foobar"}
對象id 文檔的12字節(jié)唯一ID,類似UUID {"X" :ObjectId() }
布爾值 真或假:true或false {"x": true}
數(shù)組 值的集合或列表可以表示成數(shù)組 {"x" : ["a", "b", "c"]}
32位整數(shù) 類型不可用。JavaScript僅支持64位浮點(diǎn)數(shù),所以32位整數(shù)會被自動轉(zhuǎn)換 shell是不支持該類型的,shell中默認(rèn)會轉(zhuǎn)換成64位浮點(diǎn)數(shù)
64位整數(shù) 不支持這個(gè)類型。shell會使用一個(gè)特殊的內(nèi)嵌文檔來顯示64位整數(shù) shell是不支持該類型的,shell中默認(rèn)會轉(zhuǎn)換成64位浮點(diǎn)數(shù)
64位浮點(diǎn)數(shù) shell中的數(shù)字就是這一種類型 {"x": 3.14159,"y": 3}
null 表示空值或未定義的對象 {"x": null}
undefined 文檔中可以使用未定義類型 {"x": undefined}
符號 shell不支持,shell會將數(shù)據(jù)庫中帶符號類型的數(shù)據(jù)自動轉(zhuǎn)換成字符串 -
正則表達(dá)式 文檔中可以包含正則表達(dá)式,采用JavaScript的正則表達(dá)式語法 {"x" :/foobar/i}
代碼 文檔中還可以包含JavaScript代碼 {"x" : function() { /* …… */ }}
二進(jìn)制數(shù)據(jù) 二進(jìn)制數(shù)據(jù)可以由任意字節(jié)的數(shù)字串組成,不過在shell中無法使用 -
最大值/最小值 BSON包括一個(gè)特殊類型,表示可能的最大值/最小值。shell中沒有這個(gè)類型。 -
  • 選擇和創(chuàng)建數(shù)據(jù)庫:use 數(shù)據(jù)庫名稱(有則進(jìn)入,無則創(chuàng)建)
  • 查看(有權(quán)限查看)的所有數(shù)據(jù)庫:show dbsshow databases
  • 查看當(dāng)前正在使用的數(shù)據(jù)庫:db
  • 刪除當(dāng)前正在使用的數(shù)據(jù)庫:db.dropDatabase()
  • MongoDB 中默認(rèn)的數(shù)據(jù)庫為test,若沒有選擇數(shù)據(jù)庫,則集合存放在 test 數(shù)據(jù)庫中。
  • 數(shù)據(jù)庫名可以是滿足以下條件的任意UTF-8字符串:

1、不能是空字符串("");
2、不得含有' '(空格)、.、$、/、\\0 (空字符);
3、應(yīng)該全部小寫;
4、最多為64個(gè)字節(jié)。

  • 有一些數(shù)據(jù)庫名是保留的,可以直接訪問這些有特殊作用的數(shù)據(jù)庫:

1、admin:從權(quán)限的角度來看,這是root數(shù)據(jù)庫。要是將一個(gè)用戶添加到這個(gè)數(shù)據(jù)庫,這個(gè)用戶自動繼承所有數(shù)據(jù)庫的權(quán)限。一些特定的服務(wù)器端命令也只能從這個(gè)數(shù)據(jù)庫運(yùn)行,比如列出所有的數(shù)據(jù)庫或者關(guān)閉服務(wù)器。
2、local:此數(shù)據(jù)永遠(yuǎn)不會被復(fù)制,可以用來存儲限于本地單臺服務(wù)器的任意集合。
3、config:當(dāng)Mongo用于分片設(shè)置時(shí),config數(shù)據(jù)庫在內(nèi)部使用,用于保存分片的相關(guān)信息。

  • 顯示創(chuàng)建集合:db.createCollection(name),創(chuàng)建成功則返回{ "ok" : 1 }
  • 查看當(dāng)前數(shù)據(jù)庫的所有集合(表): show collectionsshow tables
  • 刪除當(dāng)前數(shù)據(jù)庫的某個(gè)集合:db.集合名.drop(),刪除成功則返回true,否則返回false。
  • 集合的命名規(guī)范:

1、集合名不能是空字符串""。
2、集合名不能含有 \0字符(空字符),這個(gè)字符表示集合名的結(jié)尾。
3、集合名不能以system.開頭,這是為系統(tǒng)集合保留的前綴。
4、用戶創(chuàng)建的集合名字不能含有保留字符,有些驅(qū)動程序的確支持在集合名里面包含,這是因?yàn)槟承┫到y(tǒng)生成的集合中包含該字符。除非你要訪問這種系統(tǒng)創(chuàng)建的集合,否則千萬不要在名字里出現(xiàn)$

  • 使用insert()save()方法向集合中插入一個(gè)或多個(gè)文檔,語法如下:
#單條插入
db.collection.insert(
 <document or array of documents>,
 {
  writeConcern: <document>,
  ordered: <boolean>
 }
)
#批量插入
db.collection.insertMany(
 [<document 1>, <document 2>, ...],
 {
   writeConcern: <document>,
   ordered: <boolean>
 }
)
參數(shù) 類型 描述
document document or array 要插入到集合中的文檔或文檔數(shù)組(json格式)
writeConcern document Optional. A document expressing the write concern. Omit to use the default write concern. See Write Concern. Do not explicitly set the write concern for the operation if run in a transaction. To use write concern with transactions, see Transactions and Write Concern
ordered boolean 可選。如果為真,則按順序插入數(shù)組中的文檔,如果其中一個(gè)文檔出現(xiàn)錯(cuò)誤,MongoDB將返回而不處理數(shù)組中的其余文檔。如果為假,則執(zhí)行無序插入,如果其中一個(gè)文檔出現(xiàn)錯(cuò)誤,則繼續(xù)處理數(shù)組中的主文檔。在版本2.6+中默認(rèn)為true
  • 如向comment的集合(表)中插入一條測試數(shù)據(jù),執(zhí)行后,若返回此結(jié)果:WriteResult({ "nInserted" : 1 }),則表示插入成功。
db.comment.insert({"articleid":"100000","content":"今天天氣真好,陽光明媚","userid":"1001","nickname":"Rose","createdatetime":new Date(),"likenum":NumberInt(10),"state":null})
  • comment的集合(表)中批量插入多條測試數(shù)據(jù):
db.comment.insertMany([
 {"_id":"1","articleid":"100001","content":"我們不應(yīng)該把清晨浪費(fèi)在手機(jī)上,健康很重要,一杯溫水幸福你我他。","userid":"1002","nickname":"相忘于江湖","createdatetime":new Date("2019-08-05T22:08:15.522Z"),"likenum":NumberInt(1000),"state":"1"},
 {"_id":"2","articleid":"100001","content":"我夏天空腹喝涼開水,冬天喝溫開水","userid":"1005","nickname":"伊人憔悴","createdatetime":new Date("2019-08-05T23:58:51.485Z"),"likenum":NumberInt(888),"state":"1"},
 {"_id":"3","articleid":"100001","content":"我一直喝涼開水,冬天夏天都喝。","userid":"1004","nickname":"杰克船長","createdatetime":new Date("2019-08-06T01:05:06.321Z"),"likenum":NumberInt(666),"state":"1"},
 {"_id":"4","articleid":"100001","content":"專家說不能空腹吃飯,影響健康。","userid":"1003","nickname":"凱撒","createdatetime":new Date("2019-08-06T08:18:35.288Z"),"likenum":NumberInt(2000),"state":"1"},
 {"_id":"5","articleid":"100001","content":"研究表明,剛燒開的水千萬不能喝,因?yàn)闋C嘴。","userid":"1003","nickname":"凱撒","createdatetime":new Date("2019-08-06T11:01:02.521Z"),"likenum":NumberInt(3000),"state":"1"}
]);
  • 上述語句執(zhí)行后,若返回以下結(jié)果,則表示插入成功。
{
        "acknowledged" : true,
        "insertedIds" : [
                "1",
                "2",
                "3",
                "4",
                "5"
        ]
}

1、若comment集合不存在,則會隱式創(chuàng)建。
2、mongo中的數(shù)字,默認(rèn)情況下是double類型,若要存儲整型數(shù)據(jù),必須使用函數(shù)NumberInt(整型數(shù)字),否則取值時(shí)就會出錯(cuò)。
3、插入當(dāng)前日期用new Date()
4、若插入的數(shù)據(jù)沒有指定_id,則會自動生成主鍵值,否則主鍵就是該指定的值。
5、若某字段沒值,則可以賦值為null,或不寫該字段。
6、文檔中的鍵/值對是有序的。
7、文檔中的值不僅可以是在雙引號里面的字符串,還可以是其它幾種數(shù)據(jù)類型(甚至可以是整個(gè)嵌入的文檔)。
8、MongoDB區(qū)分類型和大小寫。
9、MongoDB的文檔不能有重復(fù)的鍵。
10、文檔的鍵是字符串,除了少數(shù)例外情況,鍵可以使用任意UTF-8字符。

  • 文檔鍵的命名規(guī)范:

1、鍵不能含有\0(空字符),這個(gè)字符用來表示鍵的結(jié)尾。
2、.$有特別的意義,只有在特定環(huán)境下才能使用。
3、以下劃線_開頭的鍵是保留的(不是嚴(yán)格要求的)。

  • 在批量插入文檔時(shí),若某個(gè)文檔插入失敗,則將會終止插入操作,但已插入成功的數(shù)據(jù)不會回滾掉,可使用try-catch語句來處理異常,測試時(shí)可不用處理:
try {
db.comment.insertMany([
 {"_id":"1","articleid":"100001","content":"我們不應(yīng)該把清晨浪費(fèi)在手機(jī)上,健康很重要,一杯溫水幸福你我他。","userid":"1002","nickname":"相忘于江湖","createdatetime":new Date("2019-08-05T22:08:15.522Z"),"likenum":NumberInt(1000),"state":"1"},
 {"_id":"2","articleid":"100001","content":"我夏天空腹喝涼開水,冬天喝溫開水","userid":"1005","nickname":"伊人憔悴","createdatetime":new Date("2019-08-05T23:58:51.485Z"),"likenum":NumberInt(888),"state":"1"},
 {"_id":"3","articleid":"100001","content":"我一直喝涼開水,冬天夏天都喝。","userid":"1004","nickname":"杰克船長","createdatetime":new Date("2019-08-06T01:05:06.321Z"),"likenum":NumberInt(666),"state":"1"},
 {"_id":"4","articleid":"100001","content":"專家說不能空腹吃飯,影響健康。","userid":"1003","nickname":"凱撒","createdatetime":new Date("2019-08-06T08:18:35.288Z"),"likenum":NumberInt(2000),"state":"1"},
 {"_id":"5","articleid":"100001","content":"研究表明,剛燒開的水千萬不能喝,因?yàn)闋C嘴。","userid":"1003","nickname":"凱撒","createdatetime":new Date("2019-08-06T11:01:02.521Z"),"likenum":NumberInt(3000),"state":"1"}
]);
} catch (e) {
 print (e);
}
  • 查詢文檔的語法:db.集合名.find(<query>, [projection])
參數(shù) 類型 描述
query document 可選。使用查詢運(yùn)算符指定選擇篩選器。若要返回集合中的所有文檔,請省略此參數(shù)或傳遞空文檔{}。
projection document 可選。指定要在與查詢篩選器匹配的文檔中返回的字段(投影)。若要返回匹配文檔中的所有字段,則省略此參數(shù)。
  • 查詢comment集合的所有文檔:db.comment.find()db.comment.find({})。
  • 查詢userid為1003的文檔:db.comment.find({userid: '1003'})db.comment.findOne({userid: '1003'}),其中findOne()方法返回多個(gè)文檔中的第一個(gè)文檔。
  • 若要查詢結(jié)果返回部分字段,則需要使用投影查詢(不顯示所有字段,只顯示指定的字段)。如:
db.comment.find({userid:"1003"},{userid:1,nickname:1}) #默認(rèn)顯示主鍵_id
db.comment.find({userid:"1003"},{userid:1,nickname:1,_id:0}) #不顯示主鍵_id
  • 更新文檔的語法:
db.collection.update(
 <query>,
 <update>,
 {
  upsert: <boolean>,
  multi: <boolean>,
  writeConcern: <document>,
  collation: <document>,
  arrayFilters: [ <filterdocument1>, ... ],
  hint:  <document|string> 
 }
)
參數(shù) 類型 描述
query document update的查詢條件
update document or pipeline update的對象和一些更新的操作等
upsert boolean 可選。若不存在update的記錄,是否插入objNew -> true為插入,默認(rèn)是false不插入
multi boolean 可選。默認(rèn)值為false,即只更新找到的第一個(gè)文檔。若這個(gè)參數(shù)值為true,則把按條件查出來多個(gè)文檔全部更新
writeConcern document 可選。表示寫問題的文檔。拋出異常的級別。
collation document 可選。指定要用于操作的校對規(guī)則。
arrayFilters array 可選。一個(gè)篩選文檔數(shù)組,用于確定要為數(shù)組字段上的更新操作修改哪些數(shù)組元素。
hint document or string 可選。指定用于支持查詢謂詞索引的文檔或字符串。
  • 覆蓋修改文檔:修改_id=1的文檔為點(diǎn)贊量為1001,執(zhí)行后會發(fā)現(xiàn)這條文檔除了 likenum字段其它字段都不見了。
> db.comment.update({_id:"1"},{likenum:NumberInt(1001)})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
  • 局部修改文檔,使用修改器$set來實(shí)現(xiàn),如修改_id=2的文檔中瀏覽量為889。
> db.comment.update({_id:"2"},{$set:{likenum:NumberInt(889)}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
  • 批量修改文檔:更新所有用戶為1003 的用戶的昵稱為 凱撒大帝 。
#默認(rèn)只更新符合條件的第一條記錄
> db.comment.update({userid:"1003"},{$set:{nickname:"凱撒2"}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
#修改所有符合條件的數(shù)據(jù)
> db.comment.update({userid:"1003"},{$set:{nickname:"凱撒大帝"}},{multi:true})
WriteResult({ "nMatched" : 2, "nUpserted" : 0, "nModified" : 2 })
  • 列值增長地修改文檔:使用$inc運(yùn)算符來實(shí)現(xiàn),如每次對_id=3的文檔中點(diǎn)贊數(shù)加1。
> db.comment.update({_id:"3"},{$inc:{likenum:NumberInt(1)}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
  • 刪除文檔的語法:db.集合名.remove(條件),如刪除_id=1的文檔,或者刪除comment集合中的全部文檔:
> db.comment.remove({_id:"1"})
WriteResult({ "nRemoved" : 1 })
> db.comment.remove({})
WriteResult({ "nRemoved" : 4 })
  • 統(tǒng)計(jì)集合中文檔數(shù)量的語法:db.collection.count(query, options)
參數(shù) 類型 描述
query document 查詢選擇條件
options document 可選。用于修改計(jì)數(shù)的額外選項(xiàng)
  • 統(tǒng)計(jì)集合中的文檔數(shù)量:db.comment.count()
  • 統(tǒng)計(jì)userid為1003的文檔個(gè)數(shù):db.comment.count({userid:"1003"})
  • 分頁查詢文檔的語法:db.COLLECTION_NAME.find().limit(NUMBER).skip(NUMBER),其中limit()方法用來讀取指定數(shù)量的文檔,skip()方法用來跳過指定數(shù)量的文檔。
  • 返回comment集合中前4個(gè)文檔:db.comment.find().limit(4)
  • 返回comment集合中第3和第4個(gè)文檔:db.comment.find().skip(2).limit(2)
  • 排序查詢文檔:用sort()方法對文檔進(jìn)行排序,通過參數(shù)指定排序的字段,使用 1 和 -1 來指定排序的方式,其中 1 為升序排列,而 -1 是用于降序排列。語法為:db.集合名.find().sort(排序方式),如對userid降序排列,并對訪問量進(jìn)行升序排列:
db.comment.find({},{userid:1}).sort({userid:-1,likenum:1})
  • 注意:skip(),limilt(),sort()三個(gè)方法放在一起執(zhí)行時(shí),執(zhí)行的順序是先 sort(),然后是skip(),最后是limit(),和命令編寫順序無關(guān)。
  • MongoDB的模糊查詢是通過正則表達(dá)式的方式來實(shí)現(xiàn)的。格式為:db.集合名.find({字段:/正則表達(dá)式/})
  • 查詢評論內(nèi)容中包含“開水”的所有文檔:db.comment.find({content:/開水/})
  • 查詢評論內(nèi)容中以 “專家”開頭的所有文檔:db.comment.find({content:/^專家/})
  • 比較查詢文檔:

1、db.集合名.find({ "field" : { $gt: value }}) // 大于: field > value
2、db.集合名.find({ "field" : { $lt: value }}) // 小于: field < value
3、db.集合名.find({ "field" : { $gte: value }}) // 大于等于: field >= value
4、db.集合名.find({ "field" : { $lte: value }}) // 小于等于: field <= value
5、db.集合名.find({ "field" : { $ne: value }}) // 不等于: field != value

  • 查詢評論點(diǎn)贊數(shù)量大于 700的所有文檔:db.comment.find({likenum:{$gt:NumberInt(700)}})
  • 包含使用$in操作符來查詢文檔,如查詢評論的集合中userid字段值為1003或1004的所有文檔:db.comment.find({userid:{$in:["1003","1004"]}})
  • 不包含使用$nin操作符來查詢文檔,如:查詢評論集合中userid字段值不為1003和1004的所有文檔:db.comment.find({userid:{$nin:["1003","1004"]}})。
  • 條件連接查詢文檔:①若要查詢同時(shí)滿足兩個(gè)以上條件,則需使用$and操作符將條件進(jìn)行關(guān)聯(lián),語法為:$and:[{ },{ },{}]。如查詢評論集合中 likenum大于等于700 且小于2000的所有文檔:db.comment.find({$and:[{likenum:{$gte:NumberInt(700)}},{likenum:{$lt:NumberInt(2000)}}]})
  • 若兩個(gè)以上條件之間是或者的關(guān)系,則使用$or操作符進(jìn)行關(guān)聯(lián),語法為:$or:[{ },{ },{}]。如查詢評論集合中 userid為1003,或者點(diǎn)贊數(shù)小于1000的所有文檔:db.comment.find({$or:[ {userid:"1003"}, {likenum:{$lt:1000} }]})
  • MongoDB的索引使用B-Tree,而MySQL的索引使用的是B+Tree這種數(shù)據(jù)結(jié)構(gòu)。
  • MongoDB支持在文檔的單個(gè)字段上創(chuàng)建用戶定義的升序/降序索引,稱為單字段索引(Single Field Index)。
  • MongoDB還支持多個(gè)字段的用戶定義索引,即復(fù)合索引(Compound Index)。復(fù)合索引中列出的字段順序具有重要意義:若復(fù)合索引由{ userid: 1, score: -1 } 組成,則索引首先按userid正序排序,然后在相同userid的文檔中,再在按score倒序排序。
  • 地理空間索引(Geospatial Index):為了支持對地理空間坐標(biāo)數(shù)據(jù)的有效查詢,MongoDB提供了兩種特殊的索引:返回結(jié)果時(shí)使用平面幾何的二維索引和返回結(jié)果時(shí)使用球面幾何的二維球面索引。
  • 文本索引(Text Indexes):MongoDB支持在集合中搜索字符串內(nèi)容,這些文本索引不存儲特定于語言的停止詞(例如“the”、“a”、“or”),而將集合中的詞作為詞干,只存儲根詞。
  • 哈希索引(Hashed Indexes):為了支持基于散列的分片,MongoDB提供了散列索引類型,它對字段值的散列進(jìn)行索引。這些索引在其范圍內(nèi)的值分布更加隨機(jī),但只支持相等匹配,不支持基于范圍的查詢。
  • 返回一個(gè)集合中的所有索引的數(shù)組:db.集合名.getIndexes()
> db.comment.getIndexes()
[
        {
                "v" : 2,
                "key" : {
                        "_id" : 1
                },
                "name" : "_id_",
                "ns" : "articledb.comment"
        }
]
  • 默認(rèn)_id索引:MongoDB在創(chuàng)建集合的過程中,在_id字段上創(chuàng)建一個(gè)唯一的索引,默認(rèn)名字為_id_,該索引可防止客戶端插入兩個(gè)具有相同值的文檔,不能在_id字段上刪除此索引。注意:該索引是唯一索引,因此值不能重復(fù),即_id值不能重復(fù)。在分片集群中,通常使用_id作為片鍵。
  • 在集合上創(chuàng)建索引的語法:db.集合名.createIndex(keys, options)
參數(shù) 類型 描述
keys document 包含字段和值對的文檔,其中字段是索引鍵,值描述該字段的索引類型。對于字段上的升序索引,請指定值為1;對于降序索引,請指定值為-1。如:{ 字段:1或-1}
options document 可選。包含一組控制索引創(chuàng)建的選項(xiàng)的文檔。有關(guān)詳細(xì)信息,請參見選項(xiàng)詳情列表。
  • options列表:
參數(shù) 類型 描述
background Boolean 建索引過程會阻塞其它數(shù)據(jù)庫操作,background可指定以后臺方式創(chuàng)建索引,即增加"background" 可選參數(shù)。 "background" 默認(rèn)值為false。
unique Boolean 建立的索引是否唯一,指定為true時(shí)創(chuàng)建唯一索引。默認(rèn)值為false。
name string 若未指定索引的名稱,則通過連接索引的字段名和排序順序值生成一個(gè)索引名稱。
dropDups Boolean 3.0+版本已廢棄。在建立唯一索引時(shí)是否刪除重復(fù)記錄,指定為true時(shí)創(chuàng)建唯一索引。默認(rèn)值為false。
sparse Boolean 對文檔中不存在的字段數(shù)據(jù)不啟用索引。注意:若將其設(shè)置為true,則在索引字段中不會查詢出不包含對應(yīng)字段的文檔。默認(rèn)值為 false.
expireAfterSeconds integer 設(shè)定集合的生存時(shí)間(單位:秒/s)。
v index version 索引的版本號。默認(rèn)的索引版本號取決于mongod創(chuàng)建索引時(shí)運(yùn)行的版本號。
weights document 索引權(quán)重值,數(shù)值在 1 到 99999 之間,表示該索引相對于其它索引字段的得分權(quán)重。
default_language string 對于文本索引,該參數(shù)決定了停用詞及詞干和詞器的規(guī)則的列表。 默認(rèn)為英語。
language_override string 對于文本索引,該參數(shù)指定了包含在文檔中的字段名,語言覆蓋默認(rèn)的language,默認(rèn)值為language。
  • 單字段索引示例:對 userid 字段的值按升序建立索引:
> db.comment.createIndex({userid:1})
{
        "createdCollectionAutomatically" : false,
        "numIndexesBefore" : 1,
        "numIndexesAfter" : 2,
        "ok" : 1
}
  • 復(fù)合索引:對 userid 和 nickname 同時(shí)建立復(fù)合(Compound)索引:
> db.comment.createIndex({userid:1,nickname:-1})
{
        "createdCollectionAutomatically" : false,
        "numIndexesBefore" : 2,
        "numIndexesAfter" : 3,
        "ok" : 1
}
> db.comment.getIndexes()
[
        {
                "v" : 2,
                "key" : {
                        "_id" : 1
                },
                "name" : "_id_",
                "ns" : "articledb.comment"
        },
        {
                "v" : 2,
                "key" : {
                        "userid" : 1
                },
                "name" : "userid_1",
                "ns" : "articledb.comment"
        },
        {
                "v" : 2,
                "key" : {
                        "userid" : 1,
                        "nickname" : -1
                },
                "name" : "userid_1_nickname_-1",
                "ns" : "articledb.comment"
        }
]
  • 移除集合中指定的索引,語法為:db.集合名.dropIndex(索引名稱)
  • 刪除comment集合中 userid 字段上的升序索引:
> db.comment.dropIndex({userid:1})
{ "nIndexesWas" : 3, "ok" : 1 }
  • 移除集合中所有的索引,語法為:db.集合名.dropIndexes()。注意:只能刪除非_id字段的索引。如:刪除comment集合中所有的索引:
> db.comment.dropIndexes()
{
        "nIndexesWas" : 2,
        "msg" : "non-_id indexes dropped for collection",
        "ok" : 1
}
> db.comment.getIndexes()
[
        {
                "v" : 2,
                "key" : {
                        "_id" : 1
                },
                "name" : "_id_",
                "ns" : "articledb.comment"
        }
]
  • 查看執(zhí)行計(jì)劃,語法為:db.集合名.find(query, options).explain(options),如:查看根據(jù)userid查詢數(shù)據(jù)的情況:
> db.comment.find({userid:"1003"}).explain()
{
        "queryPlanner" : {
                "plannerVersion" : 1,
                "namespace" : "articledb.comment",
                "indexFilterSet" : false,
                "parsedQuery" : {
                        "userid" : {
                                "$eq" : "1003"
                        }
                },
                "queryHash" : "37A12FC3",
                "planCacheKey" : "7FDF74EC",
                "winningPlan" : {
                        "stage" : "FETCH",
                        "inputStage" : {
                                "stage" : "IXSCAN",
                                "keyPattern" : {
                                        "userid" : 1
                                },
                                "indexName" : "userid_1",
                                "isMultiKey" : false,
                                "multiKeyPaths" : {
                                        "userid" : [ ]
                                },
                                "isUnique" : false,
                                "isSparse" : false,
                                "isPartial" : false,
                                "indexVersion" : 2,
                                "direction" : "forward",
                                "indexBounds" : {
                                        "userid" : [
                                                "[\"1003\", \"1003\"]"
                                        ]
                                }
                        }
                },
                "rejectedPlans" : [ ]
        },
        "serverInfo" : {
                "host" : "DESKTOP-NIFA5F0",
                "port" : 27017,
                "version" : "4.2.8-54-g9bffa93",
                "gitVersion" : "9bffa938ea7e8a18ba0e5508f126f6e723d063a9"
        },
        "ok" : 1
}
  • 關(guān)鍵點(diǎn)看:"stage" : "COLLSCAN",表示全集合掃描; "stage" : "IXSCAN",基于索引進(jìn)行掃描。
  • 覆蓋查詢:當(dāng)查詢條件和查詢的投影僅包含索引字段時(shí),MongoDB直接從索引返回結(jié)果,而不掃描任何文檔或?qū)⑽臋n帶入內(nèi)存。
  • MongoDB中的副本集(Replica Set)是一組維護(hù)相同數(shù)據(jù)集的mongod服務(wù)。 副本集可提供冗余和高可用性,是所有生產(chǎn)部署的基礎(chǔ)。副本集類似于有自動故障恢復(fù)功能的主從集群。
  • 冗余和數(shù)據(jù)可用性:復(fù)制提供冗余并提高數(shù)據(jù)可用性。通過在不同數(shù)據(jù)庫服務(wù)器上提供多個(gè)數(shù)據(jù)副本,復(fù)制可提供一定級別的容錯(cuò)功能,以防止單個(gè)數(shù)據(jù)庫服務(wù)器丟失數(shù)據(jù)。
  • MongoDB中的復(fù)制:副本集是一組維護(hù)相同數(shù)據(jù)集的mongod實(shí)例。 副本集包含多個(gè)數(shù)據(jù)承載節(jié)點(diǎn)和可選的一個(gè)仲裁節(jié)點(diǎn)。在承載數(shù)據(jù)的節(jié)點(diǎn)中,有且僅有一個(gè)成員被視為主節(jié)點(diǎn),其余被視為從節(jié)點(diǎn)。主節(jié)點(diǎn)接收所有的寫操作,即記錄操作日志中的數(shù)據(jù)集的所有更改:oplog
  • 從節(jié)點(diǎn)復(fù)制主節(jié)點(diǎn)的oplog并將操作應(yīng)用于其數(shù)據(jù)集,以使從節(jié)點(diǎn)的數(shù)據(jù)集反映主節(jié)點(diǎn)的數(shù)據(jù)集。
  • 主從復(fù)制和副本集區(qū)別:副本集沒有固定的"主節(jié)點(diǎn)",整個(gè)集群會選出一個(gè)"主節(jié)點(diǎn)",當(dāng)其掛掉后,又在剩余的從節(jié)點(diǎn)中選舉某個(gè)節(jié)點(diǎn)作為"主節(jié)點(diǎn)",副本集總有一個(gè)活躍點(diǎn)(主、primary)和一個(gè)或多個(gè)備份節(jié)點(diǎn)(從、secondary)。
  • 副本集有兩種類型三種角色:
    • 兩種類型:
      • 主節(jié)點(diǎn)(Primary)類型:數(shù)據(jù)操作的主要連接點(diǎn),可讀寫。
      • 從節(jié)點(diǎn)(Secondaries)類型:數(shù)據(jù)冗余備份節(jié)點(diǎn),可以讀或選舉。
    • 三種角色:
      • 主節(jié)點(diǎn)(Primary):主要接收所有寫操作。
      • 從節(jié)點(diǎn)(Replicate):從主節(jié)點(diǎn)中通過復(fù)制操作以維護(hù)相同的數(shù)據(jù)集,即備份數(shù)據(jù),不可進(jìn)行寫操作,但可以讀操作(需要配置)。
      • 仲裁者(Arbiter):不保留任何數(shù)據(jù)的副本,只具有投票選舉作用??梢詫⒅俨梅?wù)器維護(hù)為副本集的一部分,即副本成員同時(shí)也可以是仲裁者,也是一種從節(jié)點(diǎn)類型。
  • 關(guān)于仲裁者的額外說明:

1、可以將額外的mongod實(shí)例添加到副本集作為仲裁者。仲裁者不維護(hù)數(shù)據(jù)集,仲裁者的目的是通過響應(yīng)其它副本集成員的心跳和選舉請求來維護(hù)副本集中的仲裁。因?yàn)樗鼈儾淮鎯?shù)據(jù)集,所以仲裁器可以是提供副本集仲裁功能的好方法,其資源成本比具有數(shù)據(jù)集的全功能副本集成員更便宜。
2、若副本集中有偶數(shù)個(gè)成員,則需添加仲裁者以獲得主要選舉中的"大多數(shù)"投票。 仲裁者不需要專用硬件。仲裁者將永遠(yuǎn)是仲裁者,而主要人員可能會退出并成為次要人員,而次要人員可能成為選舉期間的主要人員。
3、若副本+主節(jié)點(diǎn)的個(gè)數(shù)是偶數(shù),建議加一個(gè)仲裁者,形成奇數(shù)個(gè),容易滿足大多數(shù)的投票。
4、若副本+主節(jié)點(diǎn)的個(gè)數(shù)是奇數(shù),可以不加仲裁者。

  • 副本集架構(gòu)目標(biāo):一主一副本一仲裁。

副本集的創(chuàng)建

創(chuàng)建主節(jié)點(diǎn)

  • 建立存放數(shù)據(jù)和日志的目錄:
mkdir -p /opt/mongodb/replica_sets/myrs_27017/logs \ & 
mkdir -p /opt/mongodb/replica_sets/myrs_27017/data/db \ & 
mkdir -p /opt/mongodb/replica_sets/myrs_27017/conf
  • 新建配置文件:vim /opt/mongodb/replica_sets/myrs_27017/conf/mongod.conf。注意空格!
 systemLog:
    destination: file
    path: /opt/mongodb/replica_sets/myrs_27017/logs/mongod.log
    logAppend: true
 storage:
    dbPath: /opt/mongodb/replica_sets/myrs_27017/data/db
    journal:
        enabled: true
 processManagement:
    fork: true
    pidFilePath: /opt/mongodb/replica_sets/myrs_27017/logs/mongod.pid
 net:
    bindIp: localhost,192.168.0.128
    port: 27017
 replication:
    replSetName: myrs
  • 啟動主節(jié)點(diǎn)服務(wù):
[root@dev mongodb]# /opt/mongodb/bin/mongod -f /opt/mongodb/replica_sets/myrs_27017/conf/mongod.conf
about to fork child process, waiting until server is ready for connections.
forked process: 22336
child process started successfully, parent exiting

創(chuàng)建副本節(jié)點(diǎn)

  • 建立存放數(shù)據(jù)和日志的目錄:
mkdir -p /opt/mongodb/replica_sets/myrs_27018/logs \ & 
mkdir -p /opt/mongodb/replica_sets/myrs_27018/data/db \ & 
mkdir -p /opt/mongodb/replica_sets/myrs_27018/conf
  • 新建配置文件:vim /opt/mongodb/replica_sets/myrs_27018/conf/mongod.conf。注意空格!
 systemLog:
    destination: file
    path: /opt/mongodb/replica_sets/myrs_27018/logs/mongod.log
    logAppend: true
 storage:
    dbPath: /opt/mongodb/replica_sets/myrs_27018/data/db
    journal:
        enabled: true
 processManagement:
    fork: true
    pidFilePath: /opt/mongodb/replica_sets/myrs_27018/logs/mongod.pid
 net:
    bindIp: localhost,192.168.0.128
    port: 27018
 replication:
    replSetName: myrs
  • 啟動主節(jié)點(diǎn)服務(wù):
[root@dev mongodb]# /opt/mongodb/bin/mongod -f /opt/mongodb/replica_sets/myrs_27018/conf/mongod.conf
about to fork child process, waiting until server is ready for connections.
forked process: 22382
child process started successfully, parent exiting

創(chuàng)建仲裁節(jié)點(diǎn)

  • 建立存放數(shù)據(jù)和日志的目錄:
mkdir -p /opt/mongodb/replica_sets/myrs_27019/logs \ & 
mkdir -p /opt/mongodb/replica_sets/myrs_27019/data/db \ & 
mkdir -p /opt/mongodb/replica_sets/myrs_27019/conf
  • 新建配置文件:vim /opt/mongodb/replica_sets/myrs_27019/conf/mongod.conf。注意空格!
 systemLog:
    destination: file
    path: /opt/mongodb/replica_sets/myrs_27019/logs/mongod.log
    logAppend: true
 storage:
    dbPath: /opt/mongodb/replica_sets/myrs_27019/data/db
    journal:
        enabled: true
 processManagement:
    fork: true
    pidFilePath: /opt/mongodb/replica_sets/myrs_27019/logs/mongod.pid
 net:
    bindIp: localhost,192.168.0.128
    port: 27019
 replication:
    replSetName: myrs
  • 啟動主節(jié)點(diǎn)服務(wù):
[root@dev mongodb]# /opt/mongodb/bin/mongod -f /opt/mongodb/replica_sets/myrs_27019/conf/mongod.conf
about to fork child process, waiting until server is ready for connections.
forked process: 22429
child process started successfully, parent exiting

初始化配置副本集和主節(jié)點(diǎn)

  • 使用客戶端命令連接任意一個(gè)節(jié)點(diǎn),但盡量要連接主節(jié)點(diǎn)(27017節(jié)點(diǎn)):/opt/mongodb/bin/mongo --port=27017,結(jié)果連接上之后會發(fā)現(xiàn)很多命令無法使用,比如show dbs等,必須初始化副本集才行。
  • 準(zhǔn)備初始化新的副本集,語法為:rs.initiate(configuration)
參數(shù) 類型 描述
configuration document Optional. A document that specifies configuration for the new replica set. If a configuration is not specified,MongoDB uses a default replica set configuration.
  • 使用默認(rèn)的配置來初始化副本集:rs.initiate()
    • 若"ok"的值為1,則說明創(chuàng)建成功。
    • 命令執(zhí)行后命令行提示符發(fā)生變化,變成了一個(gè)從節(jié)點(diǎn)角色,此時(shí)默認(rèn)不能讀寫,稍等片刻后回車,變成主節(jié)點(diǎn)。
> rs.initiate()
{
    "info2" : "no configuration specified. Using a default configuration for the set",
    "me" : "192.168.0.128:27017",
    "ok" : 1,
    "$clusterTime" : {
        "clusterTime" : Timestamp(1610359342, 1),
        "signature" : {
            "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
            "keyId" : NumberLong(0)
        }
    },
    "operationTime" : Timestamp(1610359342, 1)
}
myrs:SECONDARY> 
myrs:PRIMARY>
  • 查看副本集的配置內(nèi)容,語法:rs.conf(configuration)。rs.config()是該方法的別名。參數(shù)configuration可選,若沒有配置,則使用默認(rèn)主節(jié)點(diǎn)的配置。
myrs:PRIMARY> rs.conf()
{
    "_id" : "myrs",
    "version" : 1,
    "term" : 1,
    "protocolVersion" : NumberLong(1),
    "writeConcernMajorityJournalDefault" : true,
    "members" : [
        {
            "_id" : 0,
            "host" : "192.168.0.128:27017",
            "arbiterOnly" : false,
            "buildIndexes" : true,
            "hidden" : false,
            "priority" : 1,
            "tags" : {
                
            },
            "slaveDelay" : NumberLong(0),
            "votes" : 1
        }
    ],
    "settings" : {
        "chainingAllowed" : true,
        "heartbeatIntervalMillis" : 2000,
        "heartbeatTimeoutSecs" : 10,
        "electionTimeoutMillis" : 10000,
        "catchUpTimeoutMillis" : -1,
        "catchUpTakeoverDelayMillis" : 30000,
        "getLastErrorModes" : {
            
        },
        "getLastErrorDefaults" : {
            "w" : 1,
            "wtimeout" : 0
        },
        "replicaSetId" : ObjectId("5ffc222e8f297cc24be48002")
    }
}
  • 說明:① "_id" : "myrs":副本集的配置數(shù)據(jù)存儲的主鍵值,默認(rèn)就是副本集的名字;②"members": []:副本集成員數(shù)組,此時(shí)只有一個(gè):"host" : "192.168.0.128:27017";該成員不是仲裁節(jié)點(diǎn):"arbiterOnly" : false;優(yōu)先級(權(quán)重值):"priority" : 1;③"settings":副本集的參數(shù)配置。
  • 查看副本集狀態(tài),語法為:rs.status()。說明:此輸出使用從副本集的其它成員發(fā)送的心跳包中獲得的數(shù)據(jù)反映副本集的當(dāng)前狀態(tài)。
myrs:PRIMARY> rs.status()
{
    "set" : "myrs",
    "date" : ISODate("2021-01-11T10:11:06.345Z"),
    "myState" : 1,
    "term" : NumberLong(1),
    "syncSourceHost" : "",
    "syncSourceId" : -1,
    "heartbeatIntervalMillis" : NumberLong(2000),
    "majorityVoteCount" : 1,
    "writeMajorityCount" : 1,
    "votingMembersCount" : 1,
    "writableVotingMembersCount" : 1,
    "optimes" : {
        "lastCommittedOpTime" : {
            "ts" : Timestamp(1610359862, 1),
            "t" : NumberLong(1)
        },
        "lastCommittedWallTime" : ISODate("2021-01-11T10:11:02.751Z"),
        "readConcernMajorityOpTime" : {
            "ts" : Timestamp(1610359862, 1),
            "t" : NumberLong(1)
        },
        "readConcernMajorityWallTime" : ISODate("2021-01-11T10:11:02.751Z"),
        "appliedOpTime" : {
            "ts" : Timestamp(1610359862, 1),
            "t" : NumberLong(1)
        },
        "durableOpTime" : {
            "ts" : Timestamp(1610359862, 1),
            "t" : NumberLong(1)
        },
        "lastAppliedWallTime" : ISODate("2021-01-11T10:11:02.751Z"),
        "lastDurableWallTime" : ISODate("2021-01-11T10:11:02.751Z")
    },
    "lastStableRecoveryTimestamp" : Timestamp(1610359822, 1),
    "electionCandidateMetrics" : {
        "lastElectionReason" : "electionTimeout",
        "lastElectionDate" : ISODate("2021-01-11T10:02:22.706Z"),
        "electionTerm" : NumberLong(1),
        "lastCommittedOpTimeAtElection" : {
            "ts" : Timestamp(0, 0),
            "t" : NumberLong(-1)
        },
        "lastSeenOpTimeAtElection" : {
            "ts" : Timestamp(1610359342, 1),
            "t" : NumberLong(-1)
        },
        "numVotesNeeded" : 1,
        "priorityAtElection" : 1,
        "electionTimeoutMillis" : NumberLong(10000),
        "newTermStartDate" : ISODate("2021-01-11T10:02:22.725Z"),
        "wMajorityWriteAvailabilityDate" : ISODate("2021-01-11T10:02:22.758Z")
    },
    "members" : [
        {
            "_id" : 0,
            "name" : "192.168.0.128:27017",
            "health" : 1,
            "state" : 1,
            "stateStr" : "PRIMARY",
            "uptime" : 4143,
            "optime" : {
                "ts" : Timestamp(1610359862, 1),
                "t" : NumberLong(1)
            },
            "optimeDate" : ISODate("2021-01-11T10:11:02Z"),
            "syncSourceHost" : "",
            "syncSourceId" : -1,
            "infoMessage" : "",
            "electionTime" : Timestamp(1610359342, 2),
            "electionDate" : ISODate("2021-01-11T10:02:22Z"),
            "configVersion" : 1,
            "configTerm" : 1,
            "self" : true,
            "lastHeartbeatMessage" : ""
        }
    ],
    "ok" : 1,
    "$clusterTime" : {
        "clusterTime" : Timestamp(1610359862, 1),
        "signature" : {
            "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
            "keyId" : NumberLong(0)
        }
    },
    "operationTime" : Timestamp(1610359862, 1)
}
  • 說明:①"set" : "myrs":副本集的名字;②"myState" : 1:說明狀態(tài)正常;③"members":副本集成員數(shù)組,此時(shí)只有一個(gè):"name" : "192.168.0.128:27017",該成員的角色是"stateStr" : "PRIMARY" ,該節(jié)點(diǎn)是健康的:"health" : 1。

添加副本從節(jié)點(diǎn)

  • 在主節(jié)點(diǎn)中添加從節(jié)點(diǎn),將其它成員加入到副本集,語法為:rs.add(host, arbiterOnly)
參數(shù) 類型 描述
host string or document 要添加到副本集的新成員,指定為字符串或配置文檔:①若是一個(gè)字符串,則需指定新成員的主機(jī)名和可選的端口號;②若是一個(gè)文檔,則指定在members數(shù)組中找到的副本集成員配置文檔。必須在成員配置文檔中指定主機(jī)字段。有關(guān)文檔配置字段的說明,詳見下方文檔:"主機(jī)成員的配置文檔"
arbiterOnly boolean 可選。 僅在 <host> 值為字符串時(shí)適用。若為true,則添加的主機(jī)是仲裁者。
  • 主機(jī)成員的配置文檔:
{
 _id: <int>,
 host: <string>,     #required
 arbiterOnly: <boolean>,
 buildIndexes: <boolean>,
 hidden: <boolean>,
 priority: <number>,
 tags: <document>,
 slaveDelay: <int>,
 votes: <number>
}
  • 將27018的副本節(jié)點(diǎn)添加到副本集中,"ok" : 1:說明添加成功。
myrs:PRIMARY> rs.add("公網(wǎng)ip:27018")
{
    "ok" : 1,
    "$clusterTime" : {
        "clusterTime" : Timestamp(1610365858, 1),
        "signature" : {
            "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
            "keyId" : NumberLong(0)
        }
    },
    "operationTime" : Timestamp(1610365858, 1)
}

添加仲裁從節(jié)點(diǎn)

  • 添加一個(gè)仲裁節(jié)點(diǎn)到副本集,語法:rs.addArb(host)
  • 將27019的仲裁節(jié)點(diǎn)添加到副本集中:
myrs:PRIMARY> rs.addArb("公網(wǎng)ip:27019")
{
    "ok" : 1,
    "$clusterTime" : {
        "clusterTime" : Timestamp(1610366033, 1),
        "signature" : {
            "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
            "keyId" : NumberLong(0)
        }
    },
    "operationTime" : Timestamp(1610366033, 1)
}
  • 查看副本集狀態(tài):
myrs:PRIMARY> rs.status()
{
    "set" : "myrs",
    "date" : ISODate("2021-01-11T11:54:52.719Z"),
    "myState" : 1,
    "term" : NumberLong(1),
    "syncSourceHost" : "",
    "syncSourceId" : -1,
    "heartbeatIntervalMillis" : NumberLong(2000),
    "majorityVoteCount" : 2,
    "writeMajorityCount" : 2,
    "votingMembersCount" : 3,
    "writableVotingMembersCount" : 2,
    "optimes" : {
        "lastCommittedOpTime" : {
            "ts" : Timestamp(1610366082, 1),
            "t" : NumberLong(1)
        },
        "lastCommittedWallTime" : ISODate("2021-01-11T11:54:42.875Z"),
        "readConcernMajorityOpTime" : {
            "ts" : Timestamp(1610366082, 1),
            "t" : NumberLong(1)
        },
        "readConcernMajorityWallTime" : ISODate("2021-01-11T11:54:42.875Z"),
        "appliedOpTime" : {
            "ts" : Timestamp(1610366082, 1),
            "t" : NumberLong(1)
        },
        "durableOpTime" : {
            "ts" : Timestamp(1610366082, 1),
            "t" : NumberLong(1)
        },
        "lastAppliedWallTime" : ISODate("2021-01-11T11:54:42.875Z"),
        "lastDurableWallTime" : ISODate("2021-01-11T11:54:42.875Z")
    },
    "lastStableRecoveryTimestamp" : Timestamp(1610366062, 1),
    "electionCandidateMetrics" : {
        "lastElectionReason" : "electionTimeout",
        "lastElectionDate" : ISODate("2021-01-11T10:02:22.706Z"),
        "electionTerm" : NumberLong(1),
        "lastCommittedOpTimeAtElection" : {
            "ts" : Timestamp(0, 0),
            "t" : NumberLong(-1)
        },
        "lastSeenOpTimeAtElection" : {
            "ts" : Timestamp(1610359342, 1),
            "t" : NumberLong(-1)
        },
        "numVotesNeeded" : 1,
        "priorityAtElection" : 1,
        "electionTimeoutMillis" : NumberLong(10000),
        "newTermStartDate" : ISODate("2021-01-11T10:02:22.725Z"),
        "wMajorityWriteAvailabilityDate" : ISODate("2021-01-11T10:02:22.758Z")
    },
    "members" : [
        {
            "_id" : 0,
            "name" : "192.168.0.128:27017",
            "health" : 1,
            "state" : 1,
            "stateStr" : "PRIMARY",
            "uptime" : 10369,
            "optime" : {
                "ts" : Timestamp(1610366082, 1),
                "t" : NumberLong(1)
            },
            "optimeDate" : ISODate("2021-01-11T11:54:42Z"),
            "syncSourceHost" : "",
            "syncSourceId" : -1,
            "infoMessage" : "",
            "electionTime" : Timestamp(1610359342, 2),
            "electionDate" : ISODate("2021-01-11T10:02:22Z"),
            "configVersion" : 3,
            "configTerm" : 1,
            "self" : true,
            "lastHeartbeatMessage" : ""
        },
        {
            "_id" : 1,
            "name" : "公網(wǎng)ip:27018",
            "health" : 1,
            "state" : 2,
            "stateStr" : "SECONDARY",
            "uptime" : 234,
            "optime" : {
                "ts" : Timestamp(1610366082, 1),
                "t" : NumberLong(1)
            },
            "optimeDurable" : {
                "ts" : Timestamp(1610366082, 1),
                "t" : NumberLong(1)
            },
            "optimeDate" : ISODate("2021-01-11T11:54:42Z"),
            "optimeDurableDate" : ISODate("2021-01-11T11:54:42Z"),
            "lastHeartbeat" : ISODate("2021-01-11T11:54:51.055Z"),
            "lastHeartbeatRecv" : ISODate("2021-01-11T11:54:51.038Z"),
            "pingMs" : NumberLong(1),
            "lastHeartbeatMessage" : "",
            "syncSourceHost" : "192.168.0.128:27017",
            "syncSourceId" : 0,
            "infoMessage" : "",
            "configVersion" : 3,
            "configTerm" : 1
        },
        {
            "_id" : 2,
            "name" : "公網(wǎng)ip:27019",
            "health" : 1,
            "state" : 7,
            "stateStr" : "ARBITER",
            "uptime" : 59,
            "lastHeartbeat" : ISODate("2021-01-11T11:54:51.085Z"),
            "lastHeartbeatRecv" : ISODate("2021-01-11T11:54:51.090Z"),
            "pingMs" : NumberLong(1),
            "lastHeartbeatMessage" : "",
            "syncSourceHost" : "",
            "syncSourceId" : -1,
            "infoMessage" : "",
            "configVersion" : 3,
            "configTerm" : 1
        }
    ],
    "ok" : 1,
    "$clusterTime" : {
        "clusterTime" : Timestamp(1610366082, 1),
        "signature" : {
            "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
            "keyId" : NumberLong(0)
        }
    },
    "operationTime" : Timestamp(1610366082, 1)
}

副本集的數(shù)據(jù)讀寫操作

  • 登錄主節(jié)點(diǎn)27017,寫入和讀取數(shù)據(jù):
myrs:PRIMARY> use articledb
switched to db articledb
myrs:PRIMARY> db.comment.insert({"articleid":"100000","content":"今天天氣真好,陽光明媚","userid":"1001","nickname":"Rose","createdatetime":new Date()})
WriteResult({ "nInserted" : 1 })
myrs:PRIMARY> db.comment.find()
{ "_id" : ObjectId("5ffc3daf321ee60a406090d4"), "articleid" : "100000", "content" : "今天天氣真好,陽光明媚", "userid" : "1001", "nickname" : "Rose", "createdatetime" : ISODate("2021-01-11T11:59:43.728Z") }
  • 登錄從節(jié)點(diǎn)27018:/opt/mongodb/bin/mongo --port 27018
[root@dev ~]# /opt/mongodb/bin/mongo --port 27018
myrs:SECONDARY> show dbs
uncaught exception: Error: listDatabases failed:{
    "topologyVersion" : {
        "processId" : ObjectId("5ffc14e97ffa677c852d618a"),
        "counter" : NumberLong(4)
    },
    "operationTime" : Timestamp(1610366712, 1),
    "ok" : 0,
    "errmsg" : "not master and slaveOk=false",
    "code" : 13435,
    "codeName" : "NotPrimaryNoSecondaryOk",
    "$clusterTime" : {
        "clusterTime" : Timestamp(1610366712, 1),
        "signature" : {
            "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
            "keyId" : NumberLong(0)
        }
    }
} :
_getErrorWithCode@src/mongo/shell/utils.js:25:13
Mongo.prototype.getDBs/<@src/mongo/shell/mongo.js:147:19
Mongo.prototype.getDBs@src/mongo/shell/mongo.js:99:12
shellHelper.show@src/mongo/shell/utils.js:937:13
shellHelper@src/mongo/shell/utils.js:819:15
@(shellhelp2):1:1
  • 執(zhí)行show dbs后發(fā)現(xiàn)不能讀取集合的數(shù)據(jù),默認(rèn)情況下,從節(jié)點(diǎn)是沒有讀寫權(quán)限的,可以增加讀的權(quán)限,語法:rs.secondaryOk()或rs.secondaryOk(true)。
  • 在27018上設(shè)置作為從節(jié)點(diǎn)權(quán)限,具備讀權(quán)限,但不允許插入文檔:
myrs:SECONDARY> rs.secondaryOk()
myrs:SECONDARY> show dbs
admin      0.000GB
articledb  0.000GB
config     0.000GB
local      0.000GB
myrs:SECONDARY> use articledb
switched to db articledb
myrs:SECONDARY> db.comment.find()
{ "_id" : ObjectId("5ffc3daf321ee60a406090d4"), "articleid" : "100000", "content" : "今天天氣真好,陽光明媚", "userid" : "1001", "nickname" : "Rose", "createdatetime" : ISODate("2021-01-11T11:59:43.728Z") }
myrs:SECONDARY> db.comment.insert({"_id":"1","articleid":"100001","content":"我們不應(yīng)該把清晨浪費(fèi)在手機(jī)上,健康很重要,k一杯溫水幸福你我他。","userid":"1002","nickname":"相忘于江湖","createdatetime":new Date("2019-08-05T22:08:15.522Z"),"likenum":NumberInt(1000),"state":"1"})
WriteCommandError({
    "topologyVersion" : {
        "processId" : ObjectId("5ffc14e97ffa677c852d618a"),
        "counter" : NumberLong(4)
    },
    "operationTime" : Timestamp(1610367532, 1),
    "ok" : 0,
    "errmsg" : "not master",
    "code" : 10107,
    "codeName" : "NotWritablePrimary",
    "$clusterTime" : {
        "clusterTime" : Timestamp(1610367532, 1),
        "signature" : {
            "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
            "keyId" : NumberLong(0)
        }
    }
})
  • 取消作為從節(jié)點(diǎn)的讀權(quán)限:
myrs:SECONDARY> rs.secondaryOk(false)
myrs:SECONDARY> show dbs
uncaught exception: Error: listDatabases failed:{
    "topologyVersion" : {
        "processId" : ObjectId("5ffc14e97ffa677c852d618a"),
        "counter" : NumberLong(4)
    },
    "operationTime" : Timestamp(1610367382, 1),
    "ok" : 0,
    "errmsg" : "not master and slaveOk=false",
    "code" : 13435,
    "codeName" : "NotPrimaryNoSecondaryOk",
    "$clusterTime" : {
        "clusterTime" : Timestamp(1610367382, 1),
        "signature" : {
            "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
            "keyId" : NumberLong(0)
        }
    }
} :
_getErrorWithCode@src/mongo/shell/utils.js:25:13
Mongo.prototype.getDBs/<@src/mongo/shell/mongo.js:147:19
Mongo.prototype.getDBs@src/mongo/shell/mongo.js:99:12
shellHelper.show@src/mongo/shell/utils.js:937:13
shellHelper@src/mongo/shell/utils.js:819:15
@(shellhelp2):1:1
  • 登錄不存放任何業(yè)務(wù)數(shù)據(jù)的仲裁者節(jié)點(diǎn)27019:
[root@dev ~]# /opt/mongodb/bin/mongo --port 27019
myrs:ARBITER> rs.slaveOk()
WARNING: slaveOk() is deprecated and may be removed in the next major release. Please use secondaryOk() instead.
myrs:ARBITER> rs.secondaryOk()
myrs:ARBITER> show dbs
uncaught exception: Error: listDatabases failed:{
    "topologyVersion" : {
        "processId" : ObjectId("5ffc14f0bb6f0453b549db22"),
        "counter" : NumberLong(1)
    },
    "ok" : 0,
    "errmsg" : "node is not in primary or recovering state",
    "code" : 13436,
    "codeName" : "NotPrimaryOrSecondary"
} :
_getErrorWithCode@src/mongo/shell/utils.js:25:13
Mongo.prototype.getDBs/<@src/mongo/shell/mongo.js:147:19
Mongo.prototype.getDBs@src/mongo/shell/mongo.js:99:12
shellHelper.show@src/mongo/shell/utils.js:937:13
shellHelper@src/mongo/shell/utils.js:819:15
@(shellhelp2):1:1
  • 主節(jié)點(diǎn)的選舉原則:MongoDB在副本集中,會自動進(jìn)行主節(jié)點(diǎn)的選舉,主節(jié)點(diǎn)選舉的觸發(fā)條件:①主節(jié)點(diǎn)故障;②主節(jié)點(diǎn)網(wǎng)絡(luò)不可達(dá)(默認(rèn)心跳信息為10秒);③人工干預(yù)(rs.stepDown(600))。一旦觸發(fā)選舉,就要根據(jù)一定規(guī)則來選主節(jié)點(diǎn)。
  • 選舉規(guī)則是根據(jù)票數(shù)來決定誰獲勝:
    • 票數(shù)最高且獲得了"大多數(shù)"成員投票支持的節(jié)點(diǎn)獲勝。"大多數(shù)"的定義為:假設(shè)復(fù)制集內(nèi)投票成員數(shù)量為N,則大多數(shù)為 N/2 + 1。例如:3個(gè)投票成員,則大多數(shù)的值是2。當(dāng)復(fù)制集內(nèi)存活成員數(shù)量不足大多數(shù)時(shí),整個(gè)復(fù)制集將無法選舉出Primary,復(fù)制集將無法提供寫服務(wù),處于只讀狀態(tài)。
    • 若票數(shù)相同且都獲得了"大多數(shù)"成員的投票支持,數(shù)據(jù)新的節(jié)點(diǎn)獲勝。數(shù)據(jù)的新舊是通過操作日志oplog來對比的。
  • 在獲得票數(shù)時(shí),優(yōu)先級(priority)參數(shù)影響重大。優(yōu)先級即權(quán)重,取值為0-1000,相當(dāng)于可額外增加0-1000的票數(shù),優(yōu)先級的值越大,就越可能獲得多數(shù)成員的投票(votes)數(shù)。指定較高的值可使成員更有資格成為主要成員,更低的值可使成員更不符合條件。默認(rèn)情況下,優(yōu)先級的值是1。
myrs:PRIMARY> rs.conf()
{
    "_id" : "myrs",
    "version" : 3,
    "term" : 1,
    "protocolVersion" : NumberLong(1),
    "writeConcernMajorityJournalDefault" : true,
    "members" : [
        {
            "_id" : 0,
            "host" : "192.168.0.128:27017",
            "arbiterOnly" : false,
            "buildIndexes" : true,
            "hidden" : false,
            "priority" : 1,
            "tags" : {
                
            },
            "slaveDelay" : NumberLong(0),
            "votes" : 1
        },
        {
            "_id" : 1,
            "host" : "公網(wǎng)ip:27018",
            "arbiterOnly" : false,
            "buildIndexes" : true,
            "hidden" : false,
            "priority" : 1,
            "tags" : {
                
            },
            "slaveDelay" : NumberLong(0),
            "votes" : 1
        },
        {
            "_id" : 2,
            "host" : "公網(wǎng)ip:27019",
            "arbiterOnly" : true,
            "buildIndexes" : true,
            "hidden" : false,
            "priority" : 0,
            "tags" : {
                
            },
            "slaveDelay" : NumberLong(0),
            "votes" : 1
        }
    ],
    "settings" : {
        "chainingAllowed" : true,
        "heartbeatIntervalMillis" : 2000,
        "heartbeatTimeoutSecs" : 10,
        "electionTimeoutMillis" : 10000,
        "catchUpTimeoutMillis" : -1,
        "catchUpTakeoverDelayMillis" : 30000,
        "getLastErrorModes" : {
            
        },
        "getLastErrorDefaults" : {
            "w" : 1,
            "wtimeout" : 0
        },
        "replicaSetId" : ObjectId("5ffc222e8f297cc24be48002")
    }
}
  • 從執(zhí)行結(jié)果可以看出,主節(jié)點(diǎn)和副本節(jié)點(diǎn)的優(yōu)先級各為 1,即默認(rèn)已有一票,但仲裁節(jié)點(diǎn)的優(yōu)先級是0。(注意:其不能是別的值,即不具備選舉權(quán),但具有投票權(quán))

故障測試

  • 副本節(jié)點(diǎn)故障測試:關(guān)閉27018副本節(jié)點(diǎn)后發(fā)現(xiàn)主節(jié)點(diǎn)和仲裁節(jié)點(diǎn)對27018的心跳失敗。因?yàn)橹鞴?jié)點(diǎn)還在,所以沒有觸發(fā)投票選舉。若此時(shí)在主節(jié)點(diǎn)寫入數(shù)據(jù),再啟動從節(jié)點(diǎn)后發(fā)現(xiàn)主節(jié)點(diǎn)寫入的數(shù)據(jù)會自動同步給從節(jié)點(diǎn)。
db.comment.insert({"_id":"1","articleid":"100001","content":"我們不應(yīng)該把清晨浪費(fèi)在手機(jī)上,健康很重要,k一杯溫水幸福你我他。","userid":"1002","nickname":"相忘于江湖","createdatetime":new Date("2019-08-05T22:08:15.522Z"),"likenum":NumberInt(1000),"state":"1"})
  • 主節(jié)點(diǎn)故障測試:關(guān)閉27017節(jié)點(diǎn)后發(fā)現(xiàn)從節(jié)點(diǎn)和仲裁節(jié)點(diǎn)對27017的心跳失敗,當(dāng)失敗超過10秒,因?yàn)闆]有了主節(jié)點(diǎn),所以會自動發(fā)起投票。而副本節(jié)點(diǎn)只有一個(gè)(27018),因此候選人只有一個(gè)就是27018,開始投票:
    • 27019向27018投了一票,27018本身自帶一票,總共兩票,超過了"大多數(shù)";
    • 27019是仲裁節(jié)點(diǎn),沒有選舉權(quán),27018不向其投票,其票數(shù)是0;
    • 最終結(jié)果:27018成為主節(jié)點(diǎn),其具備讀寫功能;在27018寫入數(shù)據(jù)查看。
    • 再啟動 27017節(jié)點(diǎn),發(fā)現(xiàn)27017變成了從節(jié)點(diǎn),而27018仍保持主節(jié)點(diǎn)。
    • 登錄27017節(jié)點(diǎn),發(fā)現(xiàn)是從節(jié)點(diǎn)了,其數(shù)據(jù)自動從27018進(jìn)行同步,從而實(shí)現(xiàn)了高可用。
  • 仲裁節(jié)點(diǎn)和主節(jié)點(diǎn)故障:先關(guān)掉仲裁節(jié)點(diǎn)27019,再關(guān)掉主節(jié)點(diǎn)27018,登錄27017后,發(fā)現(xiàn)27017仍然是從節(jié)點(diǎn),副本集中沒有主節(jié)點(diǎn)了,導(dǎo)致此時(shí)的副本集是只讀狀態(tài),無法寫入。
    • 為啥不選舉了?因?yàn)閺墓?jié)點(diǎn)27017沒有獲得大多數(shù)的票數(shù),即小于2,它只有默認(rèn)的一票(優(yōu)先級是1)。
    • 若要觸發(fā)選舉,隨便加入一個(gè)成員即可。
    • 若只加入27019仲裁節(jié)點(diǎn)成員,則主節(jié)點(diǎn)一定是27017,因?yàn)闆]得選了,且仲裁節(jié)點(diǎn)不參與選舉,但參與投票。
    • 若只加入 27018節(jié)點(diǎn),則會發(fā)起選舉,因?yàn)?7017和27018都是兩票,此時(shí)按照誰數(shù)據(jù)新,誰當(dāng)主節(jié)點(diǎn)。
  • 仲裁節(jié)點(diǎn)和從節(jié)點(diǎn)故障:先關(guān)掉仲裁節(jié)點(diǎn)27019,再關(guān)掉副本節(jié)點(diǎn)27018,10秒后,27017主節(jié)點(diǎn)自動降級為副本節(jié)點(diǎn)。(服務(wù)降級)副本集不可寫入數(shù)據(jù)了,已經(jīng)故障了。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容