引子

最近做项目利用mongo记录的日志做数据统计。着了非关系型数据库的迷,于是乎买了本《MongoDB实战》学习了一番。记录一下学习笔记,共享之。

准备

我在自己的Linux服务器上装了最新版的Mongo。记录一下安装链接还有一个遇到的问题。

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

我想看数据库状态的时候遇到了一个权限问题

> 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)需要输入一下命令:

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

基础概念

mysql mongo 解释
database database 数据库
table collection 集合
row document 文档
column field

Mongo储存数据都是以BSON格式的,类似于JSON,如下:

{
"_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使用磁盘空间小得多,只需要将近7、8分之一的空间。记得从旧版本的mongo升级,导数据,原来20G的数据最后只有个位数(具体几G忘了),吓一跳,还担心会丢数据,原来是引擎变了。同时锁的颗粒度从数据库到文档级别,并发性能提升不少。总之,新的引擎占资源小,性能还高。

复制和分片

复制:复制是跨多个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

Mongo学习记录的更多相关文章

  1. mondb 常用命令学习记录

    mondb 常用命令学习记录 一.MongoDB 下载安装 MongoDB官网 提供了可用于 32 位和 64 位系统的预编译二进制包,你可以从MongoDB官网下载安装,MongoDB 预编译二进制 ...

  2. Quartz 学习记录1

    原因 公司有一些批量定时任务可能需要在夜间执行,用的是quartz和spring batch两个框架.quartz是个定时任务框架,spring batch是个批处理框架. 虽然我自己的小玩意儿平时不 ...

  3. Java 静态内部类与非静态内部类 学习记录.

    目的 为什么会有这篇文章呢,是因为我在学习各种框架的时候发现很多框架都用到了这些内部类的小技巧,虽然我平时写代码的时候基本不用,但是看别人代码的话至少要了解基本知识吧,另外到底内部类应该应用在哪些场合 ...

  4. Apache Shiro 学习记录4

    今天看了教程的第三章...是关于授权的......和以前一样.....自己也研究了下....我觉得看那篇教程怎么说呢.....总体上是为数不多的精品教程了吧....但是有些地方确实是讲的太少了.... ...

  5. UWP学习记录12-应用到应用的通信

    UWP学习记录12-应用到应用的通信 1.应用间通信 “共享”合约是用户可以在应用之间快速交换数据的一种方式. 例如,用户可能希望使用社交网络应用与其好友共享网页,或者将链接保存在笔记应用中以供日后参 ...

  6. UWP学习记录11-设计和UI

    UWP学习记录11-设计和UI 1.输入和设备 通用 Windows 平台 (UWP) 中的用户交互组合了输入和输出源(例如鼠标.键盘.笔.触摸.触摸板.语音.Cortana.控制器.手势.注视等)以 ...

  7. UWP学习记录10-设计和UI之控件和模式7

    UWP学习记录10-设计和UI之控件和模式7 1.导航控件 Hub,中心控件,利用它你可以将应用内容整理到不同但又相关的区域或类别中. 中心的各个区域可按首选顺序遍历,并且可用作更具体体验的起始点. ...

  8. UWP学习记录9-设计和UI之控件和模式6

    UWP学习记录9-设计和UI之控件和模式6 1.图形和墨迹 InkCanvas是接收和显示墨迹笔划的控件,是新增的比较复杂的控件,这里先不深入. 而形状(Shape)则是可以显示的各种保留模式图形对象 ...

  9. UWP学习记录8-设计和UI之控件和模式5

    UWP学习记录8-设计和UI之控件和模式5 1.日历.日期和时间控件 日期和时间控件提供了标准的本地化方法,可供用户在应用中查看并设置日期和时间值. 有四个日期和时间控件可供选择,选择的依据如下: 日 ...

随机推荐

  1. 十、CI框架之通过参数的办法输出URI路径

    一.代码如下,index函数有2个参数 二.效果如下: 不忘初心,如果您认为这篇文章有价值,认同作者的付出,可以微信二维码打赏任意金额给作者(微信号:382477247)哦,谢谢.

  2. Vue.js(18)之 axios简单封装

    基于vue-cli2.x封装axios src目录 axios.js import axios from 'axios' import { Indicator, Toast } from 'mint- ...

  3. 利用京东云Serverless服务快速构建5G时代的IoT应用

    10月31日,在2019年中国国际信息通信展览会上,工信部宣布:5G商用正式启动.5G商用时代来了! 5G的商用,使得数据传输速度.响应速度.连接数据.数据传输量.传输可靠性等方面都有了显著的提升,这 ...

  4. 2.在约会网站上使用k近邻算法

    在约会网站上使用k近邻算法 思路步骤: 1. 收集数据:提供文本文件.2. 准备数据:使用Python解析文本文件.3. 分析数据:使用Matplotlib画二维扩散图.4. 训练算法:此步骤不适用于 ...

  5. 读取cookie、写进cookie方法

    整理 读取cookie.写进cookie方法. //设置cookies中的值 function setCookie(name, value) { var Days = 30; var exp = ne ...

  6. zookeeper基础教程

    一.关于zookeeper Zookeeper 作为一个分布式的服务框架,主要用来解决分布式集群中应用系统的一致性问题,它能提供基于类似于文件系统的目录节点树方式的数据存储, Zookeeper 作用 ...

  7. UIWindow statusBar消失

    1.新建UIWindow 程序崩溃 报无根控制器错误 Xcode7环境下,新建UIWindow需添加rootViewController 2.新建UIWindow后 statusBar消失 Info. ...

  8. hadoop cmd

    一.hadoop文件操作 1.Ls hadoop fs -ls / 2.Put hadoop fs -put xx /path 3.Mkdir hadoop fs -mkdir 4.要从HDFS中删除 ...

  9. C++用libcurl通过HTTP以表单的方式Post数据到服务器

    POST字符串 #include <stdio.h> #include <curl/curl.h> int main(void) { CURL* curl = NULL; CU ...

  10. delphi数据类型列表

    Delphi 数据类型列表 分类 范围 字节 备注 简单类型 序数 整数 Integer -2147483648 .. 2147483647 4 有符号32位 Cardinal 0 .. 429496 ...