前言
MongoDB作为NoSql数据库中的典型代表,在分布式项目中广泛应用于存储格式灵活的JSON类型数据。在笔者的上一篇文章重点推荐一个基于SpringCloud的电商微服务项目mall-swarm的微服务项目中也用到了MongoDB, 因此为了深入学习这个微服务项目,咱们有必要对MongoDB有个入门的学习。至少得在自己的电脑或者服务器上安装好MongoDB服务,并学会一些基本的CRUD操作。本文笔者就来领大家学会Windows和Linux环境下安装最新稳定版本的MongDB 6.0.2版本服务,并使用学会一些基本的CRUD shell命令操作。
MongoDB简介
MongoDB是一个基于分布式文件存储的数据库。由C 语言编写,旨在为WEB应用提供可扩展的高性能数据存储解决方案。MongoDB是一个介于关系型数据库和非关系型数据库之间的产品,是非关系型数据库当中功能最丰富,最像关系型数据库的。
丰富的数据模型
MongoDB基本的思路就是将原来的"行"(row)的概念换成更加灵活的“文档”(document)模型。面向文档的方式可以将文档或者数组内嵌进来,用一条记录就可以表示非常复杂的层次关系。
容易扩展
MongoDB从最初设计的时候就考虑到了扩展的问题,它所采用的面向文档的数据模型使其可以在多台服务器之间分割数据。它还可以平衡集群的数据和负载,自动重排文档。这样开发者就可以专注于应用,而不用去考虑如何扩展。如果需要更大的容量,只需要在集群中添加新机器,然后让数据库来处理剩下的事。
丰富的功能
- 索引: MongoDB支持通用辅助索引,能进行多种快速查询,也提供唯一的、复合的和地理空间索引能力
- 存储Javascript: 开发人员不用使用存储过程,可以直接在服务端存储Javascript函数和值
- 聚合:MongoDB支持MapReduce和其他聚合工具
- 固定集合:集合的大小是有上限的,这对某些类型的数据(比如日志)特别有用
- 文件存储:MongoDB支持用一种容易使用的协议存储大型文件和文件的元数据。有些关系型数据常见的功能MongoDB并不具备,比如联接(join)和复杂的多行事务。这个架构上的考虑是为了提高扩展性,因为这两个功能实在很难在一个分布式系统上实现。
不牺牲速度
MongoDB使用MongoDB传输协议作为与服务器交互的主要方式(与之对应的协议需要更多的开销,比如HTTP/REST)。它对文档进行动态填充,预分配数据文件,用空间换取性能的稳定。默认的存储引擎中使用了内存映射文件,将内存管理工作交给了操作系统去处理。动态查询优化器会记住执行查询最高效的方式。总之MongoDB在各个方面都充分考虑了性能。
简便的管理
MongoDB尽量用服务器自治来实现数据库的管理,处了启动数据库服务器之外,几乎没有什么必要的管理操作。如果主服务器挂了,MongoDB会自动切换到备份服务器上,并且将备份服务器提升为主服务器。在分布式环境下,集群只需要知道有新增加的节点,就会自动集成和配置新节点。
MongoDB的愿景是建立一种灵活、高效、易于扩展和功能完备的数据库。
MongoDB 服务的安装
这里我们安装的MongoDB版本为最新稳定版本6.0.2版本
Windows系统安装MongoDB
1)首先去MongoDB的官网(https://www.mongodb.com/try/download/community)下载MongoDB的安装包,下载最新稳定版本6.0.2版本的msi安装包
2)然后在本地磁盘直接点击安装,进入安装向导点击Next
3) 勾选【接受协议】后点击Next
4)选择Custom安装类型
5)安装目录选择默认值C盘下的Program Files MongoDB Server6.0,然后对点击OK->Next
6)进入服务配置界面配置数据和日志目录,服务方式选择run network service user, 然后点击Next
7)点击两次Next后进入install 安装界面,点击install开始安装,安装过程需要等几分钟
8)弹出如下界面表示安装完成
安装完成之后我们会看到电脑上也安装了MongoDB Compass, 它是一个MongoDB的客户端连接工具
点击Connect就可以就可以连接上MongoDB服务进行图形化界面操作了
我们可以看到MongoDB自带3个数据库,分别是admin、config和local(上图中的admin.article集合是笔者之前创建的)
选中admin数据库点击右边的 号按钮就可以创建一个集合Collection(相当于关系型数据库中的表)
在弹出的界面的Collection Name下面的输入框中输入集合名称然后再点击右下角的Create Collection按钮即可创建一个集合
然后在弹出的界面中就可以向article集合中导入和插入数据了,我们点击“ADD DATA”下面下拉框中的Insert Document 插入一条Json格式的数据
代码语言:javascript复制{
"title": "Linux从入门到精通教程",
"description": "精通Linux运维,买这本书就对了",
"author": "鸟哥",
"publishDate": "2020-12-11"
}
添加完数据之后就可以在看到Documents下面数据中添加的数据,MongoDB数据库自动为我们插入的数据加上了一个"_id"字段,也是这条数据的默认索引。
Linux系统安装MongoDB
Linux系统下安装MongoDB, 我们在腾讯云云服务器的Cento OS7系统上使用docker容器安装,docker具有沙箱隔离机制,安装各种服务非常方便快捷。
笔者尝试过在Linux系统上安装MongoDB的tar包,安装过程出现各种启动失败的错误,但是后来尝试用docker安装,反而很顺利的就安装成功了,这里不得不感叹docker的神奇之处!
还没有安装Docker容器的朋友请自行参考菜鸟教程链接https://www.runoob.com/docker/centos-docker-install.html
1)从docker官方仓库拉取MongoDB 6.0.2版本镜像
代码语言:javascript复制docker pull mongo:6.0.2
2)启动mongo容器
代码语言:javascript复制docker run -p 27017:27017 --name mongo
-v /mydata/mongo/db:/data/db
-d mongo:6.0.2
- 第一行指令
docker run -p 27017:27017 --name mongo
表示启动mongo镜像服务,服务的容器端口为27017并映射到主机的27017端口,镜像服务命名为mongo - 第二行指令
-v /mydata/mongo/db:/data/db
表示将存储mongodb的数据挂载到宿主机(也就是Linux服务器主机)的/mydata/mongo/db
目录下 - 第三行指令表示运行mongo:6.0.2镜像
3)进入容器中的MongoDB客户端
代码语言:javascript复制docker exec -it mongo mongosh
这访无需鉴权,直接访问MongoDB的方式存在一定的安全隐患,其他人只需要知道主机ip和端口号就能直接连我们的MongoDB客户端。关于如何开启鉴权的方式访问MongoDB服务,笔者有机会再专门撰文详谈。在这篇MongoDB入门级的文章中我们先放一边。
Linux服务器上安装的MongoDB要对外服务还需要使用防火墙命令开放MongoDB的端口27017
代码语言:javascript复制firewall-cmd --permanent --zone=public --add-port=27017/tcp
firewall-cmd --reload
除此之外还需要在服务器的入站规则里加上MongoDB的端口27017
- 之后在
admin
集合中创建一个账号用于连接,这里创建的是基于readWrite
角色的管理员帐号
db.createUser({user: 'mall', pwd: 'mongo1234', roles: [{role: 'readWrite', db: 'admin'}]})
创建完mall用户之后就可以使用该新创建的用户登录了
db.auth('mall', 'mongo1234')
MongoDB 基本概念
MongoDB是非关系型数据库当中最像关系型数据库的,所以我们通过它与关系型数据库的对比,来了解下它的概念。
SQL概念 | MongoDB概念 | 解释/说明 |
---|---|---|
database | database | 数据库 |
table | collection | 数据库表/集合 |
row | document | 数据记录行/文档 |
column | field | 数据字段/属性 |
index | index | 索引 |
primary key | primary key | 主键,MongoDB自动将_id字段设置为主键 |
灵活的数据模式
相较于关系型数据库在创建表的时候必须在插入数据之前定义和声明表结构不同,MongoDB默认情况下不需要其Document具有相同的模式,也就是说:
- 在同一个集合(collection)中的文档(document)可以具有不同的字段(field)集合,同一个集合中相同的字段在不同文档中可以具有不同的数据类型;
- 方便改变同一个集合中document的结构,例如添加新的字段、删除已存在的字段或者改变一个字段的值为新的数据类型,甚至更新文档为一个新的数据结构
数据库操作
创建数据库
使用use
命令去创建数据库,当插入第一条数据时会创建MongoDB数据库,例如创建一个test
数据库
> use test
switched to db test
> db.article.insert({name:"MongoDB 教程"})
WriteResult({ "nInserted" : 1 })
> show dbs
admin 0.000GB
config 0.000GB
local 0.000GB
test 0.000GB
删除数据库
使用db对象中的dropDatabase()
方法来删除MongoDB数据库
> db.dropDatabase()
{ "dropped" : "test", "ok" : 1 }
> show dbs
admin 0.000GB
config 0.000GB
local 0.000GB
创建Collection
使用db对象中的createCollection()
方法来创建集合,例如创建一个article
集合
> use test
switched to db test
> db.createCollection("article")
{ "ok" : 1 }
> show collections
article
创建一个具有字段约束的collection
下面我们在test数据库下创建一个Students的集合,并使用$jsonSchema
操作符设置字段约束,示例如下:
db.createCollection("students", {
validator: {
$jsonSchema: {
bsonType: "object",
title: "Student Object Validation",
required: [ "address", "major", "name", "year" ],
properties: {
name: {
bsonType: "string",
description: "'name' must be a string and is required"
},
year: {
bsonType: "int",
minimum: 2017,
maximum: 3017,
description: "'year' must be an integer in [ 2017, 3017 ] and is required"
},
gpa: {
bsonType: [ "double" ],
description: "'gpa' must be a double if the field exists"
}
}
}
}
} )
然后创建一个访问test数据库需要鉴权的用户mallMaster
代码语言:javascript复制 db.createUser({user: 'mallMaster', pwd: 'master1234', roles:[{role:'readWrite', db:'test'}]})
然后我们插入一个文档来验证文档的约束
代码语言:javascript复制db.students.insertOne( {
name: "Alice",
year: Int32( 2019 ),
major: "History",
gpa: Int32(3),
address: {
city: "NYC",
street: "33rd Street"
}
} )
上面这条数据违反了gpa字段的约束,插入时因为字段约束出现了报错
然后插入一条符合约束的Students文档
代码语言:javascript复制db.students.insertOne( {
name: "Alice",
year: NumberInt(2019),
major: "History",
gpa: Double(3.0),
address: {
city: "NYC",
street: "33rd Street"
}
} )
这次插入文档成功
插入成功后会返回一个包含acknowledged和insertedId字段的对象,且acknowledged字段的值为true
删除集合
使用db.collection.drop()
方法来删除,MongoDB数据库集合,例如删除article
集合:
> db.article.drop()
true
> show collections
students
MongoDB 文档CRUD操作
插入文档操作
MongoDB通过collection对象的insertOne()
和insertMany()
方法来插入文档,语法如下:
db.collection.insertOne({<field1>:<value1>, <filed2>:<value2>, ...})
db.collection.insertMany([{<field1>:<value1>, <field2>: <value2>}, {<field1>:<value1>, <field2>: <value2>}])
db.collection.insertOne()
: 插入一个document到collection中, 入参是一个json对象
db.students.insertOne( {
name: "Alice",
year: NumberInt(2019),
major: "History",
gpa: Double(3.0),
address: {
city: "NYC",
street: "33rd Street"
}
} )
db.collection.insertMany()
: 插入多个document到collection中,入参是一个json数组
db.students.insertMany([{name: 'LiMing', year: NumberInt(2020), major:'Physical', gpa:Double(5.0), address: 'GuangDong ShenZhen'}, {name: 'LiJie', year: NumberInt(2021), major:'Math', address: 'GuangDong ShenZhen'}])
注意:MongoDB的插入操作在单一的文档级别是原子性操作
查询文档操作
使用db.collection.find(query, projection, options)
方法用来查询集合中的文档, 三个参数都是可选项
- 查询结合中的所有数据可直接使用
db.collection.find()
方法
例如执行mongo shell脚本:db.students.find()
返回如下结果:
[
{
_id: ObjectId("636692f59df5c17db20acbec"),
name: 'Alice',
year: 2019,
major: 'History',
gpa: 3,
address: { city: 'NYC', street: '33rd Street' }
},
{
_id: ObjectId("6366971b9df5c17db20acbed"),
name: 'LiMing',
year: 2020,
major: 'Physical',
gpa: 5,
address: 'GuangDong ShenZhen'
},
{
_id: ObjectId("6366971b9df5c17db20acbee"),
name: 'LiJie',
year: 2021,
major: 'Math',
address: 'GuangDong ShenZhen'
}
]
通过指定条件查询
通过执行db.collection.find({ <field1>: <value1>, ...})
mogo shell脚本查询
例如查询students集合中name为Alice的文档
db.students.find({name: 'Alice'})
图18 findAlice.png
使用查询操作符指定条件查询
通过执行 db.collection.find({ <field1>: { <operator1>: <value1> }, ... })
语法的mongo shell 脚本查询
如下面通过$gt操作符查找出sudents集合中year字段大于2020的文档对象
代码语言:javascript复制test> db.students.find({year: {$gt: 2020}})
[
{
_id: ObjectId("6366971b9df5c17db20acbee"),
name: 'LiJie',
year: 2021,
major: 'Math',
address: 'GuangDong ShenZhen'
}
]
MongoDB中的条件操作符
通过与SQL语句的对比来了解下
操作 | 格式 | SQL中的类似语句 |
---|---|---|
等于 | { <field>:<value>} | where <field> = <value> |
小于 | { <field>: {$lt: <value>} } | where <field> < <value> |
小于等于 | { <field>: {$lte: <value>} } | where <field> <= <value> |
大于 | { <field>: {$gt: <value>} } | where <field> > <value> |
大于等于 | { <field>: {$gte: <value>} } | where <field> >= <value> |
不等于 | { <field>: {$ne: <value>} } | where <field> != <value> |
在一个集合中 | { <field>: {$in: [<value1>, <value2>,...]}} | where <field> in (<value1>, <value2>, ...) |
正则匹配 | {<field>: {$regex: <value>}} | where <field> like %<value>% |
AND条件可以通过在find()
方法传入多个字段,以逗号隔开来实现
- 例如查询students集合中name为'LiMing' 且 major为'Physical' 的文档
test> db.students.find({name:'LiMing', major:'Physical'})
[
{
_id: ObjectId("6366971b9df5c17db20acbed"),
name: 'LiMing',
year: 2020,
major: 'Physical',
gpa: 5,
address: 'GuangDong ShenZhen'
}
]
- OR条件可以通过使用
$or
操作符实现 例如查询students集合中name为‘Alice' 或者'LiMing'的文档
test> db.students.find({name:'LiMing', major:'Physical'})
[
{
_id: ObjectId("6366971b9df5c17db20acbed"),
name: 'LiMing',
year: 2020,
major: 'Physical',
gpa: 5,
address: 'GuangDong ShenZhen'
}
]
test> db.students.find({$or:[{name:'Alice'},{name:'LiMing'}]})
[
{
_id: ObjectId("636692f59df5c17db20acbec"),
name: 'Alice',
year: 2019,
major: 'History',
gpa: 3,
address: { city: 'NYC', street: '33rd Street' }
},
{
_id: ObjectId("6366971b9df5c17db20acbed"),
name: 'LiMing',
year: 2020,
major: 'Physical',
gpa: 5,
address: 'GuangDong ShenZhen'
}
]
更新文档
为了方便演示,我们往students集合中插入两条数据
代码语言:javascript复制test> db.students.insertMany([
{name:'ZhangSan',
year:NumberInt(2019),
major:'Physical',
gpa:Double(4.5),
address: 'HuNan ChangSha'
},
{name:'WangWu',
year:NumberInt(2022),
major: 'Math',
gpa:Double(3.5),
address:'GuangDong GuangZhou'
}
])
返回文档:
代码语言:javascript复制{
acknowledged: true,
insertedIds: {
'0': ObjectId("6367cbb613f37f7890a5b694"),
'1': ObjectId("6367cbb613f37f7890a5b695")
}
}
MongoDB通过collection对象的以下三个方法来更新文档
db.collection.updateOne()
: 更新单个文档 语法如下:
db.collection.updateOne(
<filter>, // 过滤条件,与db.collection.find()中的查询参数语法一致
<update>, // 更新操作
{
upsert: <boolean>, // 为true时没有匹配上查询条件时插入一个文档
writeConcern: <document>,
collation: <document>,
arrayFilters: [ <filterdocument1>, ... ], // 指定要更新的字段集合
hint: <document|string> // Available starting in MongoDB 4.2.1
}
)
用法举例:
代码语言:javascript复制test> db.students.updateOne(
{name:'ZhangSan'},
{$set: {major:'Chemistry', year:NumberInt(2020)},
$currentDate:{lastModified:true}
})
$currentDate操作符会更新lastModified字段为最新日期,如果文档中没有这个字段则会添加这个表示最后更新日期的字段
返回文档:
代码语言:javascript复制{
acknowledged: true,
insertedId: null,
matchedCount: 1,
modifiedCount: 1,
upsertedCount: 0
}
db.collection.updateMany()
:更新多个文档
db.collection.updateMany(
<filter>, // 过滤条件
<update>, //更新操作
{
upsert: <boolean>,
writeConcern: <document>,
collation: <document>,
arrayFilters: [ <filterdocument1>, ... ],
hint: <document|string> // Available starting in MongoDB 4.2.1
}
)
用法举例:
代码语言:javascript复制db.students.updateMany({year:{$gte:NumberInt(2019)}},
{$set: {gpa:Double(6.5),
year: NumberInt(2022)
},
$currentDate:{lastModified:true}
})
返回文档:
代码语言:javascript复制{
acknowledged: true,
insertedId: null,
matchedCount: 5,
modifiedCount: 5,
upsertedCount: 0
}
db.collection.replaceOne()
: 替换文档 语法如下:
db.collection.replaceOne(
<filter>, //过滤条件
<replacement>, // 替换文档
{
upsert: <boolean>,
writeConcern: <document>,
collation: <document>,
hint: <document|string> // Available starting in 4.2.1
}
)
用法举例:
代码语言:javascript复制db.students.replaceOne({name:'WangWu'},
{name:'ZhaoLi',year:NumberInt(2021),
major:'Chemistry',
address:{province:'GunagDong', city:'ShenZhen', street:'23 road'},
gpa:Double(7.5)
})
返回文档:
代码语言:javascript复制{
acknowledged: true,
insertedId: null,
matchedCount: 1,
modifiedCount: 1,
upsertedCount: 0
}
update 操作符
操作符 | 描述 |
---|---|
$currentDate | 将指定字段的值更新为最新日期 |
$inc | 将指定字段的值加上指定的数值 |
$min | 只有当字段要跟新的新值小于字段的旧值时才更新 |
$max | 只有当字段要跟新的新值大于字段的旧值时才更新 |
$mul | 修改指定指定的值乘以一个数值 |
$rename | 重命字段 |
$set | 修改字段的值,相当于sql中的 set= |
$setOnInsert | 如果更新导致文档插入,则设置字段的值。对修改现有文档的更新操作没有影响 |
$unset | 从文档中删除指定的字段 |
其他update操作符请参考MongoDB官方文档链接:https://www.mongodb.com/docs/manual/reference/operator/update/
删除文档操作
最新版本的MongoDB提供了以下两种方法用于删除集合中的文档
db.collection.deleteOne()
: 删除单个文档,即使多个文档与查询条件匹配也只删除一个 语法如下:
db.collection.deleteOne(
<filter>, // 过滤条件,后面的参数为可选项
{
writeConcern: <document>,
collation: <document>,
hint: <document|string> // Available starting in MongoDB 4.4
}
)
用法举例:
代码语言:javascript复制db.students.deleteOne({name:'ZhangSan'})
返回文档:
代码语言:javascript复制{ acknowledged: true, deletedCount: 1 }
db.collection.deleteMany()
: 删除与查询条件匹配的所有文档
db.collection.deleteMany(
<filter>, // 过滤条件
{
writeConcern: <document>,
collation: <document>
}
)
用法举例:
代码语言:javascript复制db.students.deleteMany({year:{$lte:NumberInt(2021)}})
返回文档:
代码语言:javascript复制{ acknowledged: true, deletedCount: 1 }
老版本中的db.collection.remove()方法已过时,本文就不介绍了。
小结
本文主要介绍了有关MongoDB的几下几个知识点,为系统学习MongoDB数据库技能打下一个基础。
- 演示Windows上通过msi安装包安装MongDB,并使用MongoDB Compass客户端工具操作集合与文档;
- 演示Linux系统上使用docker容器安装MongoDB服务,相比于Windows上安装比较慢而且步骤多,使用docker容器安装MongoDB服务则方便快捷多了;
- 演示了使用mongo shell 命令操作创建数据库、创建集合、删除结合、插入文档、查询文档、更新文档和删除文档等MongoDB数据库中的CRUD基本操作。
参考文章
【1】MongoDB快速入门,掌握这些刚刚好!(https://juejin.cn/post/6844904150635921422)
【2】MongoDB CRUD Operations(https://www.mongodb.com/docs/v6.0/crud/)