Mongo學習記錄

引子

最近作項目利用mongo記錄的日誌作數據統計。着了非關係型數據庫的迷,因而乎買了本《MongoDB實戰》學習了一番。記錄一下學習筆記,共享之。html

準備

我在本身的Linux服務器上裝了最新版的Mongo。記錄一下安裝連接還有一個遇到的問題。java

Linux安裝mongo https://blog.51cto.com/13641879/2141129mysql

我想看數據庫狀態的時候遇到了一個權限問題正則表達式

> db.serverStatus()
{
	"ok" : 0,
	"errmsg" : "not authorized on admin to execute command { serverStatus: 1.0, lsid: { id: UUID(\"bbda7ede-9e92-492b-ae2f-f0f641fba261\") }, $db: \"admin\" }",
	"code" : 13,
	"codeName" : "Unauthorized"
}

解決方法:https://www.cnblogs.com/alexzhang92/p/10479105.html算法

若是看了上面那個解決方法,咱們再次進入mongo shell模式(以admin)須要輸入一下命令:sql

mongo -u 'admin' -p '123' --authenticationDatabase 'admin'mongodb

基礎概念

mysql mongo 解釋
database database 數據庫
table collection 集合
row document 文檔
column field

Mongo儲存數據都是以BSON格式的,相似於JSON,以下:shell

{
	"_id": ObjectId("5d399bb2b52d6dc8a4ff6b42"),
	"name": "pjjlt"
}

若是不指定主鍵,會默認生成一個_id(長度一共12字節),生成規則:4個字節的時間戳+3個字節的機器Id+2個字節的進程Id+3個字節的隨機數數據庫

Mongo操做

基礎操做

> show dbs #查看全部數據庫和使用狀況
admin   0.000GB
config  0.000GB
local   0.000GB

> use pjjlt #切換到pjjlt庫,發現沒有建立之
switched to db pjjlt

> db #查看當前在操做哪一個數據庫
pjjlt

> db.myCollection.insert({"name":"pjjlt"}) #在pjjlt庫的myCollection集合(沒有此集合,建立之)插入一條數據
WriteResult({ "nInserted" : 1 })

> show collections #查看本庫(pjjlt)下全部集合
myCollection

> db.myCollection.find() #查詢某集合數據
{ "_id" : ObjectId("5d399bb2b52d6dc8a4ff6b42"), "name" : "pjjlt" }

#篇幅緣由,不演示下面操做了,直接說解釋
>db.myCollection.drop() #刪除某集合
>db.dropDatabase() #刪除某數據庫
>db.serverStatus() #查看服務器狀態信息(查看引擎就在這邊看,能夠看到mongo4的默認引擎已是wiredtiger了)
>db.stats() #當前數據庫下簡單信息 能夠查看本庫下有多少集合
>db.myCollection.stats() #查看某集合的基礎信息
>db.runCommand() #能夠執行某個function()方法
>db.help() #查看數據庫層面全部操做
>db.myCollection.help() #查看集合層面全部操做
>db.listCommands() #列舉數據庫全部命令

數據插入

數據插入,可分爲insert和save方法。具體全部方法,能夠先輸入一段代碼,再按兩下tab鍵查看。數組

#兩下TAB鍵,看下有如下方法。insert能夠實現後面兩個方法的功能,即插入一條或多條
> db.myCollection.insert 
db.myCollection.insert(      db.myCollection.insertMany(  db.myCollection.insertOne(

#插入一條記錄
> db.myCollection.insert({"name":"haha"})
WriteResult({ "nInserted" : 1 })

#插入多條記錄,輸出的信息會更加詳細
> db.myCollection.insert([{"name":"hehe"},{"name":"heihei"}])
BulkWriteResult({
	"writeErrors" : [ ],
	"writeConcernErrors" : [ ],
	"nInserted" : 2,
	"nUpserted" : 0,
	"nMatched" : 0,
	"nModified" : 0,
	"nRemoved" : 0,
	"upserted" : [ ]
})

#看下myCollection集合下有多少數據
> db.myCollection.count()
4

#再看下內容,_id是自動生成的主鍵。
> db.myCollection.find()
{ "_id" : ObjectId("5d399bb2b52d6dc8a4ff6b42"), "name" : "pjjlt" }
{ "_id" : ObjectId("5d3a6bafd40e94efd747de7b"), "name" : "haha" }
{ "_id" : ObjectId("5d3a6c3fd40e94efd747de7c"), "name" : "hehe" }
{ "_id" : ObjectId("5d3a6c3fd40e94efd747de7d"), "name" : "heihei" }

#看一下save的插入功能,save能夠指定_id
#若是有_id存在則更新,沒有就是插入,功能相似insert
> db.myCollection.save({"name":"save0"})
WriteResult({ "nInserted" : 1 })

> db.myCollection.save([{"name":"save1"},{"name":"save2"}])
BulkWriteResult({
	"writeErrors" : [ ],
	"writeConcernErrors" : [ ],
	"nInserted" : 2,
	"nUpserted" : 0,
	"nMatched" : 0,
	"nModified" : 0,
	"nRemoved" : 0,
	"upserted" : [ ]
})

> db.myCollection.find()
{ "_id" : ObjectId("5d399bb2b52d6dc8a4ff6b42"), "name" : "pjjlt" }
{ "_id" : ObjectId("5d3a6bafd40e94efd747de7b"), "name" : "haha" }
{ "_id" : ObjectId("5d3a6c3fd40e94efd747de7c"), "name" : "hehe" }
{ "_id" : ObjectId("5d3a6c3fd40e94efd747de7d"), "name" : "heihei" }
{ "_id" : ObjectId("5d3a927fb4d620841817e434"), "name" : "save0" }
{ "_id" : ObjectId("5d3a92a2b4d620841817e436"), "name" : "save1" }
{ "_id" : ObjectId("5d3a92a2b4d620841817e437"), "name" : "save2" }

數據修改

數據修改有命令save和update,其中update有具備局部更新和替換更新的功能

#先看下save方法,指定_id,進行修改
> db.myCollection.save({"_id":ObjectId("5d3a92a2b4d620841817e437"),"name":"save3"})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.myCollection.find()
{ "_id" : ObjectId("5d399bb2b52d6dc8a4ff6b42"), "name" : "pjjlt" }
{ "_id" : ObjectId("5d3a6bafd40e94efd747de7b"), "name" : "haha" }
{ "_id" : ObjectId("5d3a6c3fd40e94efd747de7c"), "name" : "hehe" }
{ "_id" : ObjectId("5d3a6c3fd40e94efd747de7d"), "name" : "heihei" }
{ "_id" : ObjectId("5d3a927fb4d620841817e434"), "name" : "save0" }
{ "_id" : ObjectId("5d3a92a2b4d620841817e436"), "name" : "save1" }
{ "_id" : ObjectId("5d3a92a2b4d620841817e437"), "name" : "save3" }

#而後看update方法,同insert同樣,update能夠實現後面兩個方法
> db.myCollection.update
db.myCollection.update(      db.myCollection.updateMany(  db.myCollection.updateOne(

看下update語法

db.collection.update(
   <query>,   #update的查詢條件,相似sql update語句where後面的部分
   <update>,  #update的對象和一些更新的操做符等,也能夠理解爲sql update語句set後面的
   {
     upsert: <boolean>,  #可選,這個參數的意思是,若是不存在update的記錄,是否插入objNew,true爲插入,默認是false,不插入
     multi: <boolean>,   #可選,mongodb 默認是false,只更新找到的第一條記錄,若是這個參數爲true,就把按條件查出來多條記錄所有更新
     writeConcern: <document>  #可選,拋出異常的級別
   }
)

接着回到例子中,爲了說明方便,咱們新建一個新的集合user

#給新的集合建一個新的文檔
> db.user.insert({"username":"pjjlt","age":25})
WriteResult({ "nInserted" : 1 })
> db.user.find().pretty()
{
	"_id" : ObjectId("5d3aa1fcb4d620841817e438"),
	"username" : "pjjlt",
	"age" : 25
}

# ok,把pjjlt的年齡改爲18,使用了關鍵字$set,待會會演示不使用這個關鍵字的效果
> db.user.update({"_id" : ObjectId("5d3aa1fcb4d620841817e438")},{$set:{"age":18}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.user.find().pretty()
{
	"_id" : ObjectId("5d3aa1fcb4d620841817e438"),
	"username" : "pjjlt",
	"age" : 18
}

#若是使用了$set是局部更新,若是不使用,就是替換更新,爲了區分,咱們已跟新新的域city爲例
> db.user.update({"_id" : ObjectId("5d3aa1fcb4d620841817e438")},{"city":"SJZ"})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.user.find().pretty()
{ "_id" : ObjectId("5d3aa1fcb4d620841817e438"), "city" : "SJZ" }
#發現原始數據也沒了,由於被所有替換了

數據刪除

數據刪除可使用deleteMany、deleteOne、remove

# 兩下Tab
> db.user.delete
db.user.deleteMany(  db.user.deleteOne(

# 刪除沒數據了
> db.user.deleteOne({"_id" : ObjectId("5d3aa1fcb4d620841817e438")})
{ "acknowledged" : true, "deletedCount" : 1 }
> db.user.find().pretty()
> 

# 再增長几條數據
> db.user.find()
{ "_id" : ObjectId("5d3ab2f8b4d620841817e439"), "username" : "pjjlt", "age" : 25 }
{ "_id" : ObjectId("5d3ab2fdb4d620841817e43a"), "username" : "pjjlt1", "age" : 25 }
{ "_id" : ObjectId("5d3ab302b4d620841817e43b"), "username" : "pjjlt2", "age" : 25 }
{ "_id" : ObjectId("5d3ab322b4d620841817e43c"), "username" : "pjjlt3", "age" : 18 }

remove語法

db.collection.remove(
   <query>,    #可選,查詢條件
   {
     justOne: <boolean>,  #可選,設置爲true或者1,表示只刪除一個文檔,設置爲false,表示刪除全部匹配的文檔,默認爲false
     writeConcern: <document> #可選,拋出異常的級別
   }
)

再回到例子刪除一下。須要手動釋放磁盤空間

> db.user.remove({"age" : 25})
WriteResult({ "nRemoved" : 3 })
> db.user.find()
{ "_id" : ObjectId("5d3ab322b4d620841817e43c"), "username" : "pjjlt3", "age" : 18 }
#手動釋放磁盤空間
> db.repairDatabase() 
{ "ok" : 1 }

數據查詢

幾乎大部分的業務都和查詢有關。

範圍查詢運算符

運算符 描述
$lt 小於
$gt 大於
$lte 小於等於
$gte 大於等於

準備一個大數據集合,往numbers集合裏面添加1000個數字

#前面那三個點是自動生成的,對代碼沒有影響,請忽略
> for(i=0;i<1000;i++){
... db.numbers.save({num:i});
... }
WriteResult({ "nInserted" : 1 })

#測試一個$lt,其餘雷同
> db.numbers.find({"num":{$lt:5}})
{ "_id" : ObjectId("5d3ac7b7b4d620841817e43d"), "num" : 0 }
{ "_id" : ObjectId("5d3ac7b8b4d620841817e43e"), "num" : 1 }
{ "_id" : ObjectId("5d3ac7b8b4d620841817e43f"), "num" : 2 }
{ "_id" : ObjectId("5d3ac7b8b4d620841817e440"), "num" : 3 }
{ "_id" : ObjectId("5d3ac7b8b4d620841817e441"), "num" : 4 }

集合操做符

運算符 描述
$in 若是任意參數引用集合裏,則匹配
$all 若是全部參數再引用集合裏且被使用在包含數組的文檔中,則匹配
$nin 若是沒有參數在引用集合裏,則匹配
#插入一條記錄
> db.tools.insert({tools:["AAA","BBB","CCC","DDD","EEE"]})
WriteResult({ "nInserted" : 1 })
#查看一下
> db.tools.find()
{ "_id" : ObjectId("5d3ad78eb4d620841817e825"), "tools" : [ "AAA", "BBB", "CCC", "DDD", "EEE" ] }

#經過$in搜出來了
> db.tools.find({tools:{
... $in:["AAA","FFF","ZZZ"]
... }
... })
{ "_id" : ObjectId("5d3ad78eb4d620841817e825"), "tools" : [ "AAA", "BBB", "CCC", "DDD", "EEE" ] }

#$all搜索測試
> db.tools.find({tools:{ $all:["AAA","FFF","ZZZ"] } })
> db.tools.find({tools:{ $all:["AAA","BBB"] } })
{ "_id" : ObjectId("5d3ad78eb4d620841817e825"), "tools" : [ "AAA", "BBB", "CCC", "DDD", "EEE" ] }

#$nin搜索測試
> db.tools.find({tools:{ $nin:["AAA","BBB"] } })
> db.tools.find({tools:{ $nin:["ZZZ","YYY"] } })
{ "_id" : ObjectId("5d3ad78eb4d620841817e825"), "tools" : [ "AAA", "BBB", "CCC", "DDD", "EEE" ] }

布爾運算符

運算符 描述
$ne 不匹配參數
$not 不匹配結果
$or 有一個條件匹配就成立
$nor 全部條件都不匹配
$and 全部條件都匹配
$exists 判斷元素是否存在
#接着上面的例子,使用$ne
> db.tools.find({tools:{$ne:"AAA"}})
> db.tools.find({tools:{$ne:"ZZZ"}})
{ "_id" : ObjectId("5d3ad78eb4d620841817e825"), "tools" : [ "AAA", "BBB", "CCC", "DDD", "EEE" ] }

#$not 看就就像條件取反
> db.tools.find({tools:{$not:{$in:["AAA"]}}})
> db.tools.find({tools:{$not:{$in:["ZZZ"]}}})
{ "_id" : ObjectId("5d3ad78eb4d620841817e825"), "tools" : [ "AAA", "BBB", "CCC", "DDD", "EEE" ] }

#爲了演示下面的功能,添加新域給剛纔那個文檔
> db.tools.update({"_id" : ObjectId("5d3ad78eb4d620841817e825")},{$set:{"city":"SJZ"}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.tools.find().pretty()
{
	"_id" : ObjectId("5d3ad78eb4d620841817e825"),
	"tools" : [
		"AAA",
		"BBB",
		"CCC",
		"DDD",
		"EEE"
	],
	"city" : "SJZ"
}

#$or
> db.tools.find( {$or:[ {tools:{$in:["AAA"]}}, {"city":"BJ"} ]} )
{ "_id" : ObjectId("5d3ad78eb4d620841817e825"), "tools" : [ "AAA", "BBB", "CCC", "DDD", "EEE" ], "city" : "SJZ" }

#$nor 全部條件都不匹配
> db.tools.find( {$nor:[ {tools:{$in:["ZZZ"]}}, {"city":"BJ"} ]} )
{ "_id" : ObjectId("5d3ad78eb4d620841817e825"), "tools" : [ "AAA", "BBB", "CCC", "DDD", "EEE" ], "city" : "SJZ" }

#$and
> db.tools.find( {$and:[ {tools:{$in:["AAA"]}}, {"city":"BJ"} ]} )
> db.tools.find( {$and:[ {tools:{$in:["AAA"]}}, {"city":"SJZ"} ]} )
{ "_id" : ObjectId("5d3ad78eb4d620841817e825"), "tools" : [ "AAA", "BBB", "CCC", "DDD", "EEE" ], "city" : "SJZ" }

#$exists
# tools集合是否存在tools域
> db.tools.find({"tools":{$exists:true}})
{ "_id" : ObjectId("5d3ad78eb4d620841817e825"), "tools" : [ "AAA", "BBB", "CCC", "DDD", "EEE" ], "city" : "SJZ" }
# tools集合是否不存在tools域
> db.tools.find({"tools":{$exists:false}})
>

數組操做符

運算符 描述
$elemMatch 若是提供的全部詞語在相同的子文檔中,則匹配
$size 若是子文檔數組大小與提供的文本值相同,則匹配
#在新的集合裏面插入新的文檔
> db.products.insert({addresses:[{name:"home",city:"sjz"},{name:"work",city:"bj"}]})
WriteResult({ "nInserted" : 1 })
> db.products.find().pretty()
{
	"_id" : ObjectId("5d3ae3763d2312bc3a0be84c"),
	"addresses" : [
		{
			"name" : "home",
			"city" : "sjz"
		},
		{
			"name" : "work",
			"city" : "bj"
		}
	]
}

#$elemMatch 須要子文檔的哦
> db.products.find({addresses:{$elemMatch:{"name":"home","city":"sjz"}}})
{ "_id" : ObjectId("5d3ae3763d2312bc3a0be84c"), "addresses" : [ { "name" : "home", "city" : "sjz" }, { "name" : "work", "city" : "bj" } ] }

#size
> db.products.find({"addresses":{$size:1}})
> db.products.find({"addresses":{$size:2}})
{ "_id" : ObjectId("5d3ae3763d2312bc3a0be84c"), "addresses" : [ { "name" : "home", "city" : "sjz" }, { "name" : "work", "city" : "bj" } ] }

排序分頁分隔符

運算符 描述
$sort 排序 1 正序;-1 倒敘
$limit 顯示多少條
$skip 跳過多少數據

注意和MySQL同樣,skip操做大數的時候會很慢,須要提早縮小範圍

#使用numbers這個集合
> db.numbers.find().sort({"num":-1}).skip(33).limit(9)
{ "_id" : ObjectId("5d3ac7b8b4d620841817e803"), "num" : 966 }
{ "_id" : ObjectId("5d3ac7b8b4d620841817e802"), "num" : 965 }
{ "_id" : ObjectId("5d3ac7b8b4d620841817e801"), "num" : 964 }
{ "_id" : ObjectId("5d3ac7b8b4d620841817e800"), "num" : 963 }
{ "_id" : ObjectId("5d3ac7b8b4d620841817e7ff"), "num" : 962 }
{ "_id" : ObjectId("5d3ac7b8b4d620841817e7fe"), "num" : 961 }
{ "_id" : ObjectId("5d3ac7b8b4d620841817e7fd"), "num" : 960 }
{ "_id" : ObjectId("5d3ac7b8b4d620841817e7fc"), "num" : 959 }
{ "_id" : ObjectId("5d3ac7b8b4d620841817e7fb"), "num" : 958 }

其餘操做符

運算符 描述
$where 執行任意JavaScript來選擇文檔
$regex 匹配正則表達式
$mod[(quatient),(result)] 若是元素除以除數符合結果則匹配
$type 若是元素的類型符合指定的BSON類型則匹配
$text 容許在創建文本索引的字段上執行文本搜索
null 不是關鍵詞,不用加$,只是看一下使用
#只演示下 mod 和 regex
#接着用剛纔numbers那個集合
> db.numbers.find({"num":{$mod:[200,3]}})
{ "_id" : ObjectId("5d3ac7b8b4d620841817e440"), "num" : 3 }
{ "_id" : ObjectId("5d3ac7b8b4d620841817e508"), "num" : 203 }
{ "_id" : ObjectId("5d3ac7b8b4d620841817e5d0"), "num" : 403 }
{ "_id" : ObjectId("5d3ac7b8b4d620841817e698"), "num" : 603 }
{ "_id" : ObjectId("5d3ac7b8b4d620841817e760"), "num" : 803 }

#使用剛纔user集合,搜索全部username以p開頭的文檔
> db.user.find({"username":{$regex:/^p.*/}})
{ "_id" : ObjectId("5d3ab322b4d620841817e43c"), "username" : "pjjlt3", "age" : 18 }

#null的使用,搜索出沒有這個域的文檔,或者這個域爲null
> db.user.find({"username":null})
> db.user.find({"username111":null})
{ "_id" : ObjectId("5d3ab322b4d620841817e43c"), "username" : "pjjlt3", "age" : 18 }

聚合操做aggregate

能夠把聚合框架理解成SQL中的GROUP BY。爲了調用聚合框架就要定義一個管道。聚合管道里的每一步輸出做爲下一步的輸入。相似於流的概念。

運算符 描述
$project 指定輸出文檔裏的字段(項目)
$match 選擇要處理的字段,與find()相似
$limit 限制傳遞給下一步文檔數量
$skip 跳過必定數量的文檔
$unwind 拓展數組,爲每個數組入口生成一個輸出文檔
$group 根據key來分組文檔
$skip 排序文檔
$geoNear 選擇某個地理位置附近的文檔
$out 把管道的結果寫入某集合
$redact 控制特定數據的訪問

以上不少命令符與上面查詢命令符功能相同,$geoNear(這個貌似頗有趣,相似於圖論中迪傑斯特拉、弗洛伊德那些算法,能夠計算座標點之間的物理距離)和$redact先不過多的討論。$limit、$skip、$skip也過於簡單,上面演示過了,這裏再也不重複。看一下這些命令和關係型數據庫SQL的類比

SQL命令 聚合命令操做符
SELECT $project $group functions : $sum,$min,$avg,etc.
FROM db.collectionName.aggregate(...)
JOIN $unwind
WHERE $match
GROUP BY $group
HAVING $match

可是集合命令操做符沒必要按照以上順序進行書寫,好比能夠先寫$match進行條件篩選,而後進行其餘操做(其實,也每每建議這麼作,畢竟第一步就把大部分沒用的數據過濾了)

#爲了方便說明,新建一個集合
> db.score.find()
{ "_id" : ObjectId("5d3bac2d78b22e869eb2fd26"), "name" : "pjjlt", "age" : 25, "city" : "sjz" }
{ "_id" : ObjectId("5d3bac5878b22e869eb2fd27"), "name" : "qex", "age" : 112, "city" : "london" }
{ "_id" : ObjectId("5d3bad9078b22e869eb2fd28"), "name" : "haha", "age" : 24, "city" : "sjz" }
{ "_id" : ObjectId("5d3bada078b22e869eb2fd29"), "name" : "heihei", "age" : 25, "city" : "bj" }

#驗證$match和$project
> db.score.aggregate([
... {$match:{age:{$lt:100}}}, //條件過濾
... {$project:{"_id":0}} //不顯示主鍵
... ])
{ "name" : "pjjlt", "age" : 25, "city" : "sjz" }
{ "name" : "haha", "age" : 24, "city" : "sjz" }
{ "name" : "heihei", "age" : 25, "city" : "bj" }

#接下來分組顯示每一個城市的平均年齡
> db.score.aggregate([ {$group:{_id:"$city",avgAge:{$avg:"$age"}}} ])
{ "_id" : "bj", "avgAge" : 25 }
{ "_id" : "london", "avgAge" : 112 }
{ "_id" : "sjz", "avgAge" : 24.5 }

對於$group,對於原始數據如city、age,進行顯示的時候前面也須要加上$,如$city、$age,不然顯示爲null

另外展現一下$group中可使用的命令

命令 描述
$addToSet 爲組裏惟一的值建立一個數組
$first 組裏第一個值,只有前綴$sort纔有意義
$last 組裏最後一個值,只有前綴$sort纔有意義
$max 組裏某個字段的最大值
$min 組裏某個字段的最小值
$avg 某個字段的平均值
$push 返回組內全部值的數組。不去重複值
$sum 求組內全部值的和
#回來接着展現$out字符,而且$out字符相似於Java8中的流收集器,必須放最後執行
> db.score.aggregate([ {$group:{_id:"$city",avgAge:{$avg:"$age"}}} ,{$out:'aResult'}])

#刪除一下變量中的數據
> db.aResult.find()
{ "_id" : "bj", "avgAge" : 25 }
{ "_id" : "london", "avgAge" : 112 }
{ "_id" : "sjz", "avgAge" : 24.5 }

#最後是$unwind,接着用上次那個tools集合
> db.tools.find()
{ "_id" : ObjectId("5d3ad78eb4d620841817e825"), "tools" : [ "AAA", "BBB", "CCC", "DDD", "EEE" ], "city" : "SJZ" }

> db.tools.aggregate([
... {$project:{"_id":0}},
... {$unwind:"$tools"}
... ])
{ "tools" : "AAA", "city" : "SJZ" }
{ "tools" : "BBB", "city" : "SJZ" }
{ "tools" : "CCC", "city" : "SJZ" }
{ "tools" : "DDD", "city" : "SJZ" }
{ "tools" : "EEE", "city" : "SJZ" }

同關係型數據庫同樣,在project的時候,能夠對數據進行進一步加工,可選函數有concat、toLower、toUpper這種字符串函數;add、mod、multiply這種算數運算函數;日期運算符;邏輯運算符;集合操做符等。之後有空在總結。

原子性

mongo在事務處理方面不如mysql。好比兩個不一樣線程update的時候(+1操做)可能會出現數據被覆蓋的現象。特別介紹一個具備原子性的操做findAndModify(),總找到到修改提交是一個原子事件,期間其餘線程查詢該數據會被拒絕。
固然爲了保護數據,在業務代碼層面(好比java)能夠考慮加鎖。

> db.score.find({"name":"qex"})
{ "_id" : ObjectId("5d3bac5878b22e869eb2fd27"), "name" : "qex", "age" : 112, "city" : "london" }

#修改qex的年齡
> db.score.findAndModify({
... query:{"name":"qex"},
... update:{ $inc:{"age":+3}}
... })
{
	"_id" : ObjectId("5d3bac5878b22e869eb2fd27"),
	"name" : "qex",
	"age" : 112,
	"city" : "london"
}
> db.score.find({"name":"qex"})
{ "_id" : ObjectId("5d3bac5878b22e869eb2fd27"), "name" : "qex", "age" : 115, "city" : "london" }

總結一下更新操做符

操做符 描述
$inc 根據給定的值更改數字
$set 設置字段爲給定的值
$unset 取消設置字段
$rename 重命名字段給定的值
$setOnInsert 在upsert中,插入時設置字段
$bit 只執行按位更新字段
$ 根據查詢選擇器定位要更新的子文檔
$push 添加值到數組中
$addToSet 添加值到數組中,重複了也不處理
$pop 從數組中刪除第一個或者最後一個值
$pull 從數組中刪除匹配查詢條件的值
$pullAll 從數組中刪除多個值
$each 從push和addToSet一塊兒使用來操做多個值
$slice 從push和each一塊兒使用來縮小更新後數組的大小
$sort 從push和each、slice一塊兒使用來排序數組中的子文檔
$isolated 隔離其餘操做,不容許其餘操做交叉更新多個文檔

索引

索引分類

  1. 惟一索引

確保該數據在該集合只有一份,好比說主鍵,格式:

> db.user.createIndex({username:1},{unique:true})
{
	"createdCollectionAutomatically" : false,
	"numIndexesBefore" : 1,
	"numIndexesAfter" : 2,
	"ok" : 1
}
  1. 稀疏索引

稀疏索引能夠保證某些文檔不包含這個屬性,就不會被篩選出來。例如經過電話號碼建索引,可是集合中有的文檔電話號碼是null,在建立索引的時候就不會把這些文檔考慮進來。

> db.user.createIndex({username:1},{sparse:true})
  1. 多鍵索引

索引字段是個數字,好比tools集合中的tools域,這意味着針對這些數組任意值在索引上的查詢都會定位到文檔上

  1. 哈希索引

哈希索引的好處是可讓索引上的入口是均勻分佈的

db.user.createIndex({username:'hashed'})
  1. 地理位置索引

查詢某個地點附近的文檔,基於經緯度來儲存每一個文檔。

固然從引用域的數量來講還能夠分爲單鍵索引和複合索引,和mysql的行爲相似。

索引管理

  1. 創建索引

使用createIndex()。因爲創建索引的時候,數據量大會致使系統長時間等待,因此要能夠指定後臺索引(因爲須要大量數據,我沒實驗),還有離線索引等等。

db.user.createIndex({username:'hashed'},{background:true})

索引反覆操做可能會在內存中產生大量碎片,可使用命令進行碎片整理(索引新建之)

> db.user.reIndex()
{
	"nIndexesWas" : 2,
	"nIndexes" : 2,
	"indexes" : [
		{
			"v" : 2,
			"key" : {
				"_id" : 1
			},
			"name" : "_id_",
			"ns" : "pjjlt.user"
		},
		{
			"v" : 2,
			"unique" : true,
			"key" : {
				"username" : 1
			},
			"name" : "username_1",
			"ns" : "pjjlt.user"
		}
	],
	"ok" : 1
}
  1. 查看索引

查看某個集合的索引使用getIndexes()

> db.user.getIndexes()
[
	{
		"v" : 2,
		"key" : {
			"_id" : 1
		},
		"name" : "_id_",
		"ns" : "pjjlt.user"
	},
	{
		"v" : 2,
		"unique" : true,
		"key" : {
			"username" : 1
		},
		"name" : "username_1",
		"ns" : "pjjlt.user"
	}
]
  1. 刪除索引
> db.user.dropIndex("username_1") #名字能夠在查看索引的時候找到
{ "nIndexesWas" : 2, "ok" : 1 }

引擎

主要有兩種:MMAPV1和WiredTiger.在2.6版本以前只有MMAPV1,在3.0以後有了WiredTiger,可是默認仍是MMAPV1。本篇使用的是4版本,已經默認是WiredTiger引擎了。相對於說MMAPV1,WiredTiger使用磁盤空間小得多,只須要將近七、8分之一的空間。記得從舊版本的mongo升級,導數據,原來20G的數據最後只有個位數(具體幾G忘了),嚇一跳,還擔憂會丟數據,原來是引擎變了。同時鎖的顆粒度從數據庫到文檔級別,併發性能提高很多。總之,新的引擎佔資源小,性能還高。

圖片來自《MongoDB實戰》

複製和分片

複製:複製是跨多個Mongo服務器(節點)分佈和維護數據的方法。MongoDB能夠把數據從一個節點複製到其餘節點並在修改時進行同步。這種類型的複製是經過一個叫可複製集的機制提供。集羣中的節點配置爲自動同步數據,而且在服務器出錯十自動容災。MongDB還有一種舊的複製方法:主從複製。通常複製數據的時候,數據同步到了大部分(大於50%,好比說三臺機器中的兩臺)節點就能夠認爲複製操做完成。可複製集應該保護至少3個成員,其中一個能夠當裁判。

分片:分片是把大型數據集進行分區成更小的可管理的片的過程。簡單來講就是一臺MongoDB服務器盛不下或處理不了那麼多數據(1,2,3,4,5,6)了,就將這些數據分到三臺小點的機子上,分別維護(1,4)(2,5)(3,6)

參考

《MongoDB實戰》

菜鳥教程 https://www.runoob.com/mongodb/mongodb-tutorial.html

相關文章
相關標籤/搜索