MongoDB从入门到入土
1、数据量大
2、写入操作频繁
3、价值较低的数据,对事务性要求不高
MongoDB是一个开源、高性能、无模式的文档型数据库,是NoSQL数据库产品中的一种,是最像关系型数据库的非关系型数据库。
无模式:指MongoDB没有一个具体的列,他存储用的是类似于JSON的格式叫BSON,一种二进制的JSON文件
MongoDB中的记录是一个文档,他是一个由字段和值对(field:value)组成的数据结构。MongoDB文档类似于JSON对象即认为一个文档是一个对象。字段的数据类型是字符型,他的值除了使用基本的一些类型外,还可以包括其他文档、普通数组和文档数组。
1、高性能
2、高可用性
MongoDB的复制工具称为副本集(replica set),他可以提供自动故障转移和数据冗余
3、高扩展性
分片将数据分布在一组集群的机器上,支持海量数据存储,服务能力水平扩展
4、丰富的查询支持
MongoDB支持丰富的查询语言,支持读写操作,比如数据聚合,MongoDB将一个区域所覆盖的读写只定向到该区域内的那些片
百度云:https://pan.baidu.com/s/18au42FIhSNrXY9p7MbmNbg 提取码:29ad
解压即可使用
命令行方式:
在bin目录下创建data文件夹,在data文件夹下创建db文件夹,打开cmd命令行,执行下面命令:
mongod --dbpath=..\data\db
在bin目录下创建conf文件夹,在conf文件夹下创建mongod.conf文件,增加下面的配置:
storage:
#The directory where the mongod instance stores its data.Default Value is "\data\db" on Windows.
dbPath: E:\\MongoDB\\mongodb-win32-x86_64-2008plus-ssl-4.0.12\\data\\db
打开cmd命令行,执行下面命令:
mongod -f ..\conf\mongod.conf
compass、navicat
# 查看数据库
> show dbs
# 创建并使用数据库,此时这个库还只是在内存中,只有添加了集合之后才持久化到磁盘,使用show dbs命令才可以看得到
> use articledb
# 查看正在使用的数据库
> db
# 删除数据库,主要用来删除已经持久化的数据库
> db.dropDatabase()
有一些数据库是保留的,具有特定的作用:
当向一个集合中插入一个文档的时候,如果集合不存在,则会自动创建集合,这种方式称为隐式创建,当然你也可以显式创建。
显式创建命令:
# 创建一个集合
> db.createCollection("my")
# 查看集合
> show collections
# 删除集合
> db.my.drop()
隐式创建参考文档的操作
演示:向comment集合插入一条数据:
> user my
> db.comment.insert({"articleid":"100000","content":"今天天气真好,阳光明 媚","userid":"1001","nickname":"Rose","createdatetime":new Date(),"likenum":NumberInt(10),"state":null})
当返回下列信息时,就代表插入成功
WriteResult({ "nInserted" : 1 })
说明:
comment集合如果不存在,则会隐式创建
mongo中的数字,默认情况下是double类型,如果要存整型,必须使用函数NumberInt(整型数字),否则取出来就有问题了。
插入当前日期使用 new Date()
插入的数据没有指定 _id ,会自动生成主键值
如果某字段没值,可以赋值为null,或不写该字段。
注意:
文档中的键/值对是有序的。
文档中的值不仅可以是在双引号里面的字符串,还可以是其他几种数据类型(甚至可以是整个嵌入的文档)。
MongoDB区分类型和大小写。
MongoDB的文档不能有重复的键。文档的键是字符串。除了少数例外情况,键可以使用任意UTF-8字符。
文档键命名规范:
键不能含有\0 (空字符)。这个字符用来表示键的结尾。
.和$有特别的意义,只有在特定环境下才能使用。
以下划线"_"开头的键是保留的(不是严格要求的)。
演示:插入多条数据
db.comment.insertMany([ {"_id":"1","articleid":"100001","content":"我们不应该把清晨浪费在手机上,健康很重要,一杯温水幸福你我 他。","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":"研究表明,刚烧开的水千万不能喝,因为烫 嘴。","userid":"1003","nickname":"凯撒","createdatetime":new Date("2019-08- 06T11:01:02.521Z"),"likenum":NumberInt(3000),"state":"1"} ]);
查询所有:
> db.comment.find()
{ "_id" : ObjectId("6249a57a5e43b07ec991c633"), "articleid" : "100000", "content" : "今天天气真好,阳光明 媚", "userid" : "1001", "nickname" : "Rose", "createdatetime" : ISODate("2022-04-03T13:47:38.051Z"), "likenum" : 10, "state" : null }
这里每条文档会有一个叫_id的字段,这个相当于原来关系数据库中表的主键,当在插入文档记录时没有指定该字段,MongoDB会自动创建,其类型是ObjectID类型。
如果在插入文档记录时指定该字段也可以,其类型可以是ObjectID类型,也可以是MongoDB支持的任意类型。
查询单个:
> db.comment.find({articleid:"100001"})
{ "_id" : "1", "articleid" : "100001", "content" : "我们不应该把清晨浪费在手机上,健康很重要,一杯温水幸福你我 他。", "userid" : "1002", "nickname" : "相忘于江湖", "createdatetime" : ISODate("1970-01-01T00:00:00Z"), "likenum" : 1000, "state" : "1" }
{ "_id" : "2", "articleid" : "100001", "content" : "我夏天空腹喝凉开水,冬天喝温开水", "userid" : "1005", "nickname" : "伊人憔 悴", "createdatetime" : ISODate("2019-08-05T23:58:51.485Z"), "likenum" : 888, "state" : "1" }
{ "_id" : "3", "articleid" : "100001", "content" : "我一直喝凉开水,冬天夏天都喝。", "userid" : "1004", "nickname" : "杰克船 长", "createdatetime" : ISODate("2019-08-06T01:05:06.321Z"), "likenum" : 666, "state" : "1" }
{ "_id" : "4", "articleid" : "100001", "content" : "专家说不能空腹吃饭,影响健康。", "userid" : "1003", "nickname" : "凯 撒", "createdatetime" : ISODate("2019-08-06T08:18:35.288Z"), "likenum" : 2000, "state" : "1" }
{ "_id" : "5", "articleid" : "100001", "content" : "研究表明,刚烧开的水千万不能喝,因为烫 嘴。", "userid" : "1003", "nickname" : "凯撒", "createdatetime" : ISODate("1970-01-01T00:00:00Z"), "likenum" : 3000, "state" : "1" }
多个结果只想要一条:
> db.comment.findOne({articleid:"100001"})
{
"_id" : "1",
"articleid" : "100001",
"content" : "我们不应该把清晨浪费在手机上,健康很重要,一杯温水幸福你我 他。",
"userid" : "1002",
"nickname" : "相忘于江湖",
"createdatetime" : ISODate("1970-01-01T00:00:00Z"),
"likenum" : 1000,
"state" : "1"
}
投影查询(只显示想要的字段):
> db.comment.findOne({articleid:"100001"},{userid:1,nickname:1})
{ "_id" : "1", "userid" : "1002", "nickname" : "相忘于江湖" }
注意:
try { db.comment.insertMany([ {"_id":"1","articleid":"100001","content":"我们不应该把清晨浪费在手机上,健康很重要,一杯温水幸福你我 他。","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":"研究表明,刚烧开的水千万不能喝,因为烫 嘴。","userid":"1003","nickname":"凯撒","createdatetime":new Date("2019-08- 06T11:01:02.521Z"),"likenum":NumberInt(3000),"state":"1"} ]); } catch (e) { print (e); }
覆盖的修改:
如果想修改_id为1的记录,点赞量为1001,输入以下语句即可
db.comment.update({_id:"1"},{likenum:NumberInt(1001)})
执行后,会发现这条数据除了likenum字段其他字段不见了
局部修改:
为了解决上述的问题,需要使用修改器$set来实现,命令如下:
db.comment.update({_id:"2"},{$set:{likenum:NumberInt(889)}})
更新所有用户为1003的用户昵称为凯撒大帝
# 默认只修改第一条数据
db.comment.update({userid:"1003"},{$set:{nickname:"凯撒2"}})
# 修改所有符合条件的数据
db.comment.update({userid:"1003"},{$set:{nickname:"凯撒大帝"}},{multi:true})
列值增长的修改:
如果我们想实现某列值在原有值的基础上增加或减少,可以使用$inc运算来实现
例如:对3号数据的点赞数,每次递增1
db.comment.update({_id:"1003"},{$inc:{likenum:NumberInt(1)}})
删除所有数据:
db.comment.remove({})
删除_id=1的数据
db.comment.remove({_id:"1"})
统计查询:
(1)统计所有记录数
db.comment.count()
(2)按条件统计记录数
db.comment.count({userid:"1003"})
分页列表查询:
使用limit()方法来读取指定数量的数据,使用skip()方法来跳过指定数量的数据
如果想返回指定条数的记录,可以在find()后调用limit()来返回结果,默认值20,例如:
db.comment.find().limit(2)
skip()方法同样接受一个数字参数作为跳过的记录条数。即前N个不要,默认是0
db.comment.find.skip(1)
分页查询需求:每页2个,从第二页开始。即跳过前两条数据,接着显示第3和第条数据
db.comment.find().skip(2).limit(1)
sort()方法对数据进行排序,可以指定排序的字段,并使用1和-1来指定排序的方式,其中1为升序,-1为降序
例如:对userid进行升序,对likenum进行降序
db.comment.find().sort({userid:1,likenum:-1})
MongoDB的磨合查询是通过正则表达式的方式实现的
查询评论内容包含“开水”的文档
db.comment.find({content:/开水/})
查询评论的内容是以“专家”开头的
db.comment.find({content:/^专家/})
比较查询的格式如下:
db.集合名称.find({ "field" : { $gt: value }}) // 大于: field > value
db.集合名称.find({ "field" : { $lt: value }}) // 小于: field < value
db.集合名称.find({ "field" : { $gte: value }}) // 大于等于: field >= value
db.集合名称.find({ "field" : { $lte: value }}) // 小于等于: field <= value
db.集合名称.find({ "field" : { $ne: value }}) // 不等于: field != value
示例:查询评论点赞数量大于700的记录
db.comment.find({"likenum":{$gt:NmuberInt(700)}})
包含使用$in操作符,示例:查询评论的集合中userid字段包含1003或1004的文档
db.comment.find(userid:{$in:["1003","1004"]})
相似的,不包含使用$nin操作符即可。
如果需要查询同时满足两个以上的条件,需要使用$and操作符将条件进行关联,相当于SQL的and,格式为:
$and:[{},{},{}]
示例:查询评论集合中likenum大于等于700并且小于2000的记录
db.comment.find({$and:[{likenum:{$gte:NumberInt(700)}},{likenum:{$lt:NumberInt(2000)}}]})
如果两个条件之间是或者的关系,使用or操作符进行关联,与and的使用方式相同,格式为:
$or:[{},{},{}]
示例:查询评论集合中userid为1003,或者点赞数小于1000的记录
db.comment.find({$or:[{userid:"1003"},{likenum:{$lt:NumberInt(1000)}}]})
索引是特殊的数据结构,他以易于遍历的形式存储数据集的一小部分。索引存储特定字段或一组字段的值,按字段值排序。索引项的排序支持有效的相等匹配和基于范围的查询操作,此外,MongoDB还可以使用索引中的排序返回排序的结果。
MongoDB支持在文档的单个字段上创建用户定义的升序/降序索引,称为单字段索引。
对于单个字段索引和排序操作,索引键的排序顺序(升序或降序)并不重要,因为MongoDB可以在任何方向上遍历索引。
MongoDB还支持多个字段的索引,即复合索引
复合索引中列出的字段顺序有重要意义,例如:如果复合索引由{userid:1,score:-1}组成,则索引首先按照userid正序排列,然后在每个userid的值内,再按照score排列(类似于MySQL的最佳左前缀)
地理空间索引、文本索引、哈希索引
返回一个集合中所有索引的数组
db.comment.getIndexes()
结果显示的是默认的_id索引,MongoDB在创建集合的过程中,在_id字段上创建一个唯一的索引,默认名字为_id_,该索引可防止客户端插入两个具有相同值的文档,不能在_id字段上删除此索引。
注意:该索引是唯一所以,即_id值是不能重复的,在分片集群中,通常使用_id作为分片键
创建单字段索引,对userid字段建立升序索引:
db.comment.crateIndex({userid:1})
创建复合索引,对userid和nickname建立复合索引:
db.comment.createIndex({userid:1,nickname:-1})
删除指定的索引:
db.comment.dropIndex({userid:1})
删除所有的索引
db.collection.dropIndexes()
示例:查看根据userid查询数据的情况
> db.comment.find({userid:"1003"}).explain()
{
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "articledb.comment",
"indexFilterSet" : false,
"parsedQuery" : {
"userid" : {
"$eq" : "1003"
}
},
"winningPlan" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"userid" : 1,
"nickname" : -1
},
"indexName" : "userid_1_nickname_-1",
"isMultiKey" : false,
"multiKeyPaths" : {
"userid" : [ ],
"nickname" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"userid" : [
"[\"1003\", \"1003\"]"
],
"nickname" : [
"[MaxKey, MinKey]"
]
}
}
},
"rejectedPlans" : [ ]
},
"serverInfo" : {
"host" : "DESKTOP-HL7VBD1",
"port" : 27017,
"version" : "4.0.12",
"gitVersion" : "5776e3cbf9e7afe86e6b29e22520ffb6766e95d4"
},
"ok" : 1
}
>
关键点:“stage” : “COLLSCAN”, 表示全集合扫描
下面对userid建立唯一索引,再次查看执行计划
> db.comment.createIndex({userid:1})
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 2,
"numIndexesAfter" : 3,
"ok" : 1
}
>
>
> db.comment.find({userid:"1003"}).explain()
{
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "articledb.comment",
"indexFilterSet" : false,
"parsedQuery" : {
"userid" : {
"$eq" : "1003"
}
},
"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" : [
{
"stage" : "FETCH",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"userid" : 1,
"nickname" : -1
},
"indexName" : "userid_1_nickname_-1",
"isMultiKey" : false,
"multiKeyPaths" : {
"userid" : [ ],
"nickname" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"userid" : [
"[\"1003\", \"1003\"]"
],
"nickname" : [
"[MaxKey, MinKey]"
]
}
}
}
]
},
"serverInfo" : {
"host" : "DESKTOP-HL7VBD1",
"port" : 27017,
"version" : "4.0.12",
"gitVersion" : "5776e3cbf9e7afe86e6b29e22520ffb6766e95d4"
},
"ok" : 1
}
>
关键点:“stage” : “IXSCAN” ,基于索引的扫描
当查询条件和查询的字段仅包含索引字段时,MongoDB直接从索引返回结果,而不扫扫描任何文档或将文档带入内存。
注意:类似于MySQL的索引覆盖