MongoDB
什么是MongoDB
MongoDB是一个文档数据库(以 JSON 为数据模型),由C++语言编写,旨在为WEB应用提供可扩展的高性能数据存储解决方案。
MongoDB是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。它支持的数据结构非常松散,数据格式是BSON,一种类似JSON的二进制形式的存储格式,简称Binary JSON ,和JSON一样支持内嵌的文档对象和数组对象,因此可以存储比较复杂的数据类型。Mongo最大的特点是它支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。原则上 Oracle 和 MySQL 能做的事情,MongoDB 都能做(包括 ACID 事务)。
判断业务是否适合使用MongoDB
只要有一项需求满足就可以考虑使用MongoDB,匹配越多,选择MongoDB越合适。
安装MongoDB
Docker安装
-
第一步: 创建数据目录和配置文件
- 创建数据目录,直接将MongoDB镜像中的数据挂载到此目录
shellmkdir /Users/cheng/mongo mkdir /Users/cheng/mongo/data
- 创建配置文件mongod.conf
shellmkdir /Users/cheng/mongo/conf vim mongod.conf
shell# mongod.conf 配置文件 yml格式 storage: dbPath: /data/db net: bindIp: 0.0.0.0 port: 27017 security: authorization: enabled
-
第二步:搜索镜像并拉取
- docker search mongo
- docker pull mongo
-
第三步: 启动镜像
- docker run --name mongo1 -p 27017:27017 -v /Users/cheng/mango/data:/data/db \
-v /Users/cheng/mango/conf/mongod.conf:/etc/mongo/mongod.conf -itd mongo --config /etc/mongo/mongod.conf --auth
- docker run --name mongo1 -p 27017:27017 -v /Users/cheng/mango/data:/data/db \
-
第四步: 进入mongosh
- docker ps | grep mongo # 查询镜像id
- docker exec -it 镜像id mongosh admin
- 进入镜mongosh成功
-
第五步:创建管理员
shell# 设置管理员用户名密码需要切换到admin库 use admin #创建管理员 db.createUser({user:"root",pwd:"root",roles:[{role:"root",db:'admin'}]}) # 登录 db.auth({user:'root',pwd:'root'}) # 查看当前数据库所有用户信息 show users #显示可设置权限 show roles #显示所有用户 db.system.users.find()
- 常用权限
- 常用权限
mongosh常用命令
增、删、改、查
创建数据集
db.createCollection('emp')
插入文档
MongoDB提供了以下方法将文档插入到集合中:
- db.emp.insertOne() #插入单条
- db.emp.insertMany() #插入多条
插入单条记录
shell
# 简单实例
db.emp.insertOne({name:'test',age:22}) # 插入语句
## 下方是返回结果
{
acknowledged: true,
insertedId: ObjectId('66b8f4a06834104abef3f54e')
}
# insertOne支持设置writeConcern(写入确认选项)
db.emp.insertOne(
{ name: "test2", age: 35},
{
writeConcern: { w: "majority", j: true, wtimeout: 5000 }
}
)
writeConcern 是 MongoDB 中用来控制写入确认的选项。以下是 writeConcern 参数的一些常见选项:
- w:指定写入确认级别。如果指定为数字,则表示要等待写入操作完成的节点数。如果指定为 majority,则表示等待大多数节点完成写入操作。默认为 1,表示等待写入操作完成的节点数为 1。
- j:表示写入操作是否要求持久化到磁盘。如果设置为 true,则表示写入操作必须持久化到磁盘后才返回成功。如果设置为 false,则表示写入操作可能在数据被持久化到磁盘之前返回成功。默认为 false。
- wtimeout:表示等待写入操作完成的超时时间,单位为毫秒。如果超过指定的时间仍然没有返回确认信息,则返回错误。默认为 0,表示不设置超时时间
插入多条记录
js
// 语法格式
db.collection.insertMany(
[ <document 1> , <document 2>, ... ],
{
writeConcern: <document>,
ordered: <boolean>
}
)
// writeConcern:写入确认选项,可选。
// ordered:指定是否按顺序写入,默认 true,按顺序写入。
查询文档
js
//语法格式
db.collection.findOne(query, projection) //查询第一个文档
db.collection.find(query, projection)
// query :可选,使用查询操作符指定查询条件
// projection :可选,使用投影操作符指定返回的键。
//查询时返回文档中所有键值, 只需省略该参数即可(默认省略)。
//投影时,_id为1的时候,其他字段必须是1;_id是0的时候,其他字段可以是0;
//如果没有_id字段约束,多个其他字段必须同为0或同为1。
//如果查询返回的条目数量较多,mongosh则会自动实现分批显示。
//默认情况下每次只显示20条,可以输入it命令读取下一批。
//正则表达式
//使用正则表达式查找name包含 te 字符串的
db.emp.find({name:{$regex:"te"}})
//或者
db.emp.find({name:/te/})
//分页
//skip用于指定跳过记录数,limit则用于限定返回结果数量。
//可以在执行find命令的同时指定skip、limit参数,以此实现分页的功能。
db.emp.find().skip(2).limit(3)
-
条件转义
-
处理分页问题 -- 巧分页
- 数据量大的时候,应该避免使用skip/limit形式的分页。
- 替代方案:使用查询条件+唯一排序条件;
javascript//例如: //第一页: db.books.find({}).sort({_id: 1}).limit(10); //第二页: db.books.find({_id: {$gt: <第一页最后一个_id>}}).sort({_id: 1}).limit(10); //第三页: db.books.find({_id: {$gt: <第二页最后一个_id>}}).sort({_id: 1}).limit(10);
-
处理分页问题 -- 避免使用 count
-
尽可能不要计算总页数,特别是数据量大和查询条件不能完整命中索引时。
js// 考虑以下场景:假设集合总共有 1000w 条数据,在没有索引的情况下考虑以下查询: db.coll.find({x: 100}).limit(50); db.coll.count({x: 100});
-
前者只需要遍历前 n 条,直到找到 50 条 x=100 的文档即可结束;
-
后者需要遍历完 1000w 条找到所有符合要求的文档才能得到结果。 为了计算总页数而进行的 count() 往往是拖慢页面整体加载速度的原因
-
更新文档
MongoDB提供了以下方法来更新集合中的文档:
- db.collection.updateOne ():即使多个文档可能与指定的筛选器匹配,也只会更新第一个匹配的文档。
- db.collection.updateMany ():更新与指定筛选器匹配的所有文档。
更新操作符
更新单个文档'
js
//语法
db.collection.updateOne(
<filter>,
<update>,
{
upsert: <boolean>,
writeConcern: <document>,
collation: <document>,
arrayFilters: [ <filterdocument1>, ... ],
hint: <document|string> // Available starting in MongoDB 4.2.1
}
)
db.collection.updateOne()方法的参数含义如下:
- <filter>:一个筛选器对象,用于指定要更新的文档。只有与筛选器对象匹配的第一个文档才会被更新。
- <update>:一个更新操作对象,用于指定如何更新文档。可以使用一些操作符,例如 s e t 、 set、 set、inc、$unset等,以更新文档中的特定字段。
- upsert:一个布尔值,用于指定如果找不到与筛选器匹配的文档时是否应插入一个新文档。如果upsert为true,则会插入一个新文档。默认值为false。
- writeConcern:一个文档,用于指定写入操作的安全级别。可以指定写入操作需要到达的节点数或等待写入操作的时间。
- collation:一个文档,用于指定用于查询的排序规则。例如,可以通过指定locale属性来指定语言环境,从而实现基于区域设置的排序。
- arrayFilters:一个数组,用于指定要更新的数组元素。数组元素是通过使用更新操作符 [ ] 和 []和 []和来指定的。
- hint:一个文档或字符串,用于指定查询使用的索引。该参数仅在MongoDB 4.2.1及以上版本中可用。
注意,除了filter和update参数外,其他参数都是可选的。
更新多个文档
updateMany更新与集合的指定筛选器匹配的所有文档
js
//例如
db.emp.updateMany({name:"test"},{$set:{publishedDate:new Date()}})
findAndModify
findAndModify兼容了查询和修改指定文档的功能,findAndModify只能更新单个文档
js
//将某个文档的收藏数属性(favCount)加1
db.emp.findAndModify({
query:{_id:ObjectId("6457a39c817728350ec83b9d")},
update:{$inc:{favCount:1}}
})
默认情况下,findAndModify会返回修改前的"旧"数据。如果希望返回修改后的数据,则可以指定new选项
js
db.books.findAndModify({
query:{_id:ObjectId("6457a39c817728350ec83b9d")},
update:{$inc:{favCount:1}},
new: true //返回修改后的数据
})
与findAndModify语义相近的命令如下:
- findOneAndUpdate:更新单个文档并返回更新前(或更新后)的文档。
- findOneAndReplace:替换单个文档并返回替换前(或替换后)的文档。
删除文档
deleteOne &deleteMany
官方推荐使用 deleteOne() 和 deleteMany() 方法删除文档,语法格式如下:
js
db.emp.deleteOne ({ name:"test" }) //删除 name等于test 的一个文档
db.emp.deleteMany ({}) //删除集合下全部文档
db.emp.deleteMany ({ name:"test" }) //删除 name等于test 的全部文档
注意:remove、deleteMany命令需要对查询范围内的文档逐个删除,如果希望删除整个集合,则使用drop命令会更加高效
findOneAndDelete
deleteOne命令在删除文档后只会返回确认性的信息,如果希望获得被删除的文档,则可以使用findOneAndDelete命令
js
db.emp.findOneAndDelete({name:"test"})
除了在结果中返回删除文档,findOneAndDelete命令还允许定义"删除的顺序",即按照指定顺序删除找到的第一个文档。利用这个特性,findOneAndDelete可以实现队列的先进先出。
js
db.emp.findOneAndDelete({name:"test"},{sort:{favCount:1}})
批量操作
bulkwrite()方法提供了执行批量插入、更新和删除操作的能力。
bulkWrite()支持以下写操作:
- insertOne
- updateOne
- updateMany
- replaceOne
- deleteOne
- deleteMany
每个写操作都作为数组中的文档传递给bulkWrite()。
js
db.pizzas.insertMany( [
{ _id: 0, type: "pepperoni", size: "small", price: 4 },
{ _id: 1, type: "cheese", size: "medium", price: 7 },
{ _id: 2, type: "vegan", size: "large", price: 8 }
] )
db.pizzas.bulkWrite( [
{ insertOne: { document: { _id: 3, type: "beef", size: "medium", price: 6 } } },
{ insertOne: { document: { _id: 4, type: "sausage", size: "large", price: 10 } } },
{ updateOne: {
filter: { type: "cheese" },
update: { $set: { price: 8 } }
} },
{ deleteOne: { filter: { type: "pepperoni"} } },
{ replaceOne: {
filter: { type: "vegan" },
replacement: { type: "tofu", size: "small", price: 4 }
} }
] )