MongoDB - 文档型数据库介绍及简单的使用
1. MongoDB 相关概念
1.1 MongoDB 业务应用场景
“三高”需求:
-
High performance - 对数据库高并发读写的需求
-
Huge Storage - 对海量数据的高效存储和访问的需求
-
High Scalability && High Availability 对数据库的高可扩展性和高可用性的需求
具体场景:
-
社交场景,使用 MongoDB 存储用户信息,以及用户发表的朋友圈信息,通过地理位置索引实现附近的人,地点等功能
-
游戏场景,使用 MongoDB 存储游戏用户信息,用户的装备,积分等直接已内嵌文档的形式存储,方便查询,高效率存储和访问
-
物流场景,使用 MongoDB 存储订单信息,订单状态在运行过程中会不断更新,以 MongoDB 内嵌数组的形式来存储,一次查询就能将订单所有的变更读取出来
-
物联网场景,使用 MongoDB 存储所有接入智能设备信息,以及设备汇报的日志信息,并对这些信息进行多维度的分析
-
视频直播,使用 MongoDB 存储用户信息、点赞互动信息等
特点:
-
数据量大
-
写入操作频繁
-
价值较低的数据,对事物性要求不高
1.2 MongoDB 简介
MongoDB是一个开源,高性能,无模式的文档型数据库, 当初的设计就是用于简化开发和方便扩展, 是NoSQL数据库产品中的一种最像关系型数据库(MySQL)的非关系型数据库.
它支持的数据结构非常松散, 是一种类似于 JSON 的格式叫BSON, 所以它既可以存储比较复杂的数据类型, 又相当的灵活.
MongoDB中的记录是一个文档, 它是一个由字段和值对(field:value)组成的数据结构.MongoDB文档类似于JSON对象, 即一个文档认为就是一个对象.
字段的数据类型是字符型, 它的值除了使用基本的一些类型外, 还可以包括其他文档, 普通数组和文档数组
1.3 MongoDB 体系结构
2. 基本常用指令
2.1 数据库操作
以下是MongoDB数据库自带的数据库。
-
admin: 从权限角度考虑, 这是
root
数据库, 如果将一个用户添加到这个数据库, 这个用户自动继承所有数据库的权限, 一些特定的服务器端命令也只能从这个数据库运行, 比如列出所有的数据库或者关闭服务器 -
local: 数据永远不会被复制, 可以用来存储限于本地的单台服务器的集合 (部署集群, 分片等)
-
config: Mongo 用于分片设置时,
config
数据库在内部使用, 用来保存分片的相关信息
2.1.1 选择和创建数据库
选择和创建数据库的语法
1 | use 数据库名称 // 选择当前数据库 |
查看的所有的数据库指令
1 2 | show dbs //查看所有数据库 show databases //查看所有数据库 |
注意: 当使用 use articledb 的时候,articledb 其实存放在内存之中,当 articledb 中存在一个 collection(集合) 之后,mongo 才会将这个数据库持久化到硬盘之中(换句话说 如果在创建了数据库,但是数据库中没有集合的话,使用 show dbs 是看不见创建的数据库的)
2.1.2 数据库删除
1 2 3 | use 数据库名称 // 选择数据库 db 查看当前数据库 // 查看当前数据库名称 db.dropDatabase() // 删除当前数据库 |
2.2 集合操作
2.2.1 集合的显式创建
1 | db.createCollection(name) // 创建集合(表) |
参数说明: name:要创建的集合名称
1 2 3 | db.createCollection( "collection" ) // 创建集合(表) show collections // 查看当前库中的所有集合(表) collection show tables // 查看当前库中的所有集合(表) collection |
2.2.2 集合的隐式创建
当向一个集合中插入一个文档的时候,如果集合不存在,则会自动创建集合
2.2.3 集合的删除
1 2 | db.collection.drop() // db.集合.drop() 删除某个集合(表)<br> 案例: db.collection.drop() // 删除成功返回 true |
2.3 文档基本 CRUD
2.3.1 文档的插入
文档的数据结构和 JSON 基本一致,所有存储在集合中的数据都是 BSON 格式
-
使用
db.<collection_name>.insert()
向集合中添加一个文档, 参数一个 json 格式的文档 -
使用
db.<collection_name>.insertMany()
向集合中添加多个文档, 参数为 json 文档数组
1 2 3 4 5 6 7 8 9 10 | // 向集合中添加一个文档 db.collection.insert( { item: "canvass" , qty: 10 , tags: [ "cotton" ], size: { h: 28 , w: 35.5 , uom: "cm" } } ) // 向集合中添加多个文档 db.collection.insertMany([ { item: "journaly" , qty: 50 , tags: [ "blank" , "red" ], size: { h: 14 , w: 21 , uom: "cm" } }, { item: "mate" , qty: 90 , tags: [ "gray" ], size: { h: 27.9 , w: 35.5 , uom: "cm" } }, { item: "mousepads" , qty: 30 , tags: [ "gel" , "blue" ], size: { h: 19 , w: 22.85 , uom: "cm" } } ]) |
注:当我们向 collection
中插入 document
文档时, 如果没有给文档指定 _id
属性, 那么数据库会为文档自动添加 _id
field, 并且值类型是 ObjectId(blablabla)
, 就是文档的唯一标识, 类似于 relational database 里的 primary key (一般来说 MongoDB 应该采用文档默认自动添加的 _id field 对数据进行存储)
2.3.2 文档的批量插入
如果某条数据插入失败, 将会终止插入, 但已经插入成功的数据不会回滚掉. 因为批量插入由于数据较多容易出现失败, 因此, 可以使用 try catch
进行异常捕捉处理, 测试的时候可以不处理.如:
1 2 3 4 5 6 7 | try { db.collection.insertMany([ { "_id" : "1" , "articleid" : "100001" , "content" : "没有人真的很忙,谁的一天都是24小时,所谓忙与闲,不过是心里面觉得哪件事更重要罢了" , "userid" : "1002" , "nickname" : "Tom" , "createdatetime" : new Date( "2023-0805T22:08:15.522Z" ), "likenum" :NumberInt( 5000 ), "state" : "1" }, { "_id" : "2" , "articleid" : "100001" , "content" : "在成年人的感情世界里有一个潜规则,不主动就是答案,没有回应就等于拒绝,这世界上没有人忙到一天不回复你的消息" , "userid" : "1005" , "nickname" : "amy" , "createdatetime" : new Date( "2023-08-05T23:58:51.485Z" ), "likenum" :NumberInt( 4488 ), "state" : "1" }, { "_id" : "3" , "articleid" : "100001" , "content" : "你没有大把的时光再去蹉跎了,遇见喜欢的东西就去买,想做什么就去做,有心仪的人就去追" , "userid" : "1004" , "nickname" : "json" , "createdatetime" : new Date( "2023-08-06T01:05:06.321Z" ), "likenum" :NumberInt( 466 ), "state" : "1" }, { "_id" : "4" , "articleid" : "100001" , "content" : "一边后悔,一边生活是人生最蠢的事。" , "userid" : "1003" , "nickname" : "mile" , "createdatetime" : new Date( "2023-08-06T08:18:35.288Z" ), "likenum" :NumberInt( 3000 ), "state" : "1" }, { "_id" : "5" , "articleid" : "100001" , "content" : "不管生活多久才变好,都要先把自己变得更好" , "userid" : "1003" , "nickname" : "jock" , "createdatetime" : new Date( "2023-0806T11:01:02.521Z" ), "likenum" :NumberInt( 2000 ), "state" : "1" }<br> { "_id" : "6" , "articleid" : "100001" , "content" : "你的评论总是那么有见地,让我对这个话题有了更深入的理解和思考" , "userid" : "1003" , "nickname" : "jock" , "createdatetime" : new Date( "2023-0806T11:01:02.521Z" ), "likenum" :NumberInt( 139 ), "state" : "1" }<br>]); <em id= "__mceDel" >} catch (e) { print (e); }</em> |
2.3.3 文档查询
-
使用
db.<collection_name>.find()
方法对集合进行查询, 接受一个 json 格式的查询条件. 返回的是一个数组 -
db.<collection_name>.findOne()
查询集合中符合条件的第一个文档, 返回的是一个对象
查询数据语法:
1 | db.collection.find(<query>,[projection]) |
只显示某些字段查询:
1 2 | 只查询某个字段(画横线部分为查询条件) + 只查询部分字段(加粗部分为展示部分) db.collection.find({ "articleid" : "10001" },{articleid: 1 ,_id: 0 ,number: 1 }) |
使用 $in 操作符表示范围查询
1 | db.collection.find({status:{$in:[ 25 ]}}) |
多个查询条件用逗号分隔,表示 AND 的关系
1 | db.collection.find({status: "A" ,qty:{$lt: 30 }}) // $lt,$gt 具体看判断查询 在后面 |
使用 $or 操作符表示后边数组中的条件是 OR 的关系
1 | db.collection.find({$or:[{status: "A" },{qty:{$lt: 30 }}]}) |
联合使用 AND 和 OR 的查询语句
1 2 3 4 | db.collection.find( { status: "A" , $or: [ { qty: { $lt: 30 } }, { item: /^p/ } ] } ) |
2.2.4 更新文档
-
使用
db.<collection_name>.updateOne(<filter>, <update>, <options>)
方法修改一个匹配<filter>
条件的文档 -
使用
db.<collection_name>.updateMany(<filter>, <update>, <options>)
方法修改所有匹配<filter>
条件的文档 -
使用
db.<collection_name>.replaceOne(<filter>, <update>, <options>)
方法替换一个匹配<filter>
条件的文档 -
db.<collection_name>.update(查询对象, 新对象)
默认情况下会使用新对象替换旧对象
$set
修改文档中的制定属性
其中最常用的修改操作符即为 $set
和 $unset
,分别表示赋值和取消赋值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | db.collection.updateOne( { item: "paper" }, { $set: { "size.uom" : "cm" , status: "P" }, $currentDate: { lastModified: true } } ) db.inventory.updateMany( { qty: { $lt: 50 } }, { $set: { "size.uom" : "in" , status: "P" }, $currentDate: { lastModified: true } } ) |
批量修改
1 2 3 4 5 | // 默认会修改第一条 db.document.update({ userid: "30" , { $set {username: "guest" } } }) // 修改所有符合条件的数据 db.document.update( { userid: "30" , { $set {username: "guest" } } }, {multi: true } ) |
列值增长的增加
1 | db.document.update({ _id: "3" , {$inc: {likeNum: NumberInt( 1 )}} }) |
修改操作符
Name | Description |
---|---|
Sets the value of a field to current date, either as a Date or a Timestamp. | |
Increments the value of the field by the specified amount. | |
Only updates the field if the specified value is less than the existing field value. | |
Only updates the field if the specified value is greater than the existing field value. | |
Multiplies the value of the field by the specified amount. | |
Renames a field. | |
Sets the value of a field in a document. | |
Sets the value of a field if an update results in an insert of a document. Has no effect on update operations that modify existing documents. | |
Removes the specified field from a document. |
2.2.5 删除文档
-
使用
db.collection.deleteMany()
方法删除所有匹配的文档. -
使用
db.collection.deleteOne()
方法删除单个匹配的文档. -
db.collection.drop()
-
db.dropDatabase()
删除文档的语法结构:
1 | db.集合名称.remove(条件) |
以下语句可以将数据全部删除,请慎用
1 | db.collectionremove([]) |
如果删除 _id=1 的记录,输入以下语句
1 | db.collection.remove({_id: "1" }) |
2.4 文档的分页查询
2.4.1 统计查询
1)统计集合的所有的记录数
1 | db.collection.count() |
2)按条件统计记录数
1 | db.collection.count({userid: "1003" }) |
默认情况下 count() 方法返回符合条件的全部记录条数
2.4.2 分页列表查询
分页查询某几条记录,并跳过某几条(skip() 表示跳过前几天数据)
1 | db.comment.find().limit( 2 ).skip( 4 ) |
2.4.3 排序查询
sort() 方法对数据进行排序,sort() 方法可以通过参数指定排序的字段,并使用 1 和 -1 来指定排序的方式 其中 1为升序排序,而 -1 是用于降序排序
1 2 | db.comment.fing().sort({key: 1 ,......,......}) // db.集合名称.find().sort(排序方式) |
提示:
skip(),limit(),sort() 三个放在一起执行的时候,执行的顺序是先 sort(),然后 skip(),最后显示的 limit(),和命令编写顺序无关
2.5 文档的更多查询
2.5.1 正则的复杂条件查询
MongoDB的模糊查询是通过正则表达式的方式实现的
1 2 | db.collection.find({field:/正则表达式/}) db.collection.find({字段:/正则表达式/}) |
提示:正则表达式是 js 的语法,直接量的写法
2.5.2 比较查询
<
, <=
, >
, >=
这些操作符也是很常用的, 格式如下:
1 2 3 4 5 | db.collection.find({ "field" : { $gt: value }}) // 大于: field > value db.collection.find({ "field" : { $lt: value }}) // 小于: field < value db.collection.find({ "field" : { $gte: value }}) // 大于等于: field >= value db.collection.find({ "field" : { $lte: value }}) // 小于等于: field <= value db.collection.find({ "field" : { $ne: value }}) // 不等于: field != value |
2.5.3 包含查询
包含使用 $in 操作符
1 | db.comment.find({userid:{$in:[ "1001" , "1002" ]}}) |
不包含使用 $nin 操作符
1 | db.comment.find({userid:{$nin:[ "1003" , "1004" ]}}) |
2.5.4 条件连接查询
1 2 3 4 5 6 7 8 9 | AND 写法 $and:[{},{},{}] // 查询评论集合中 likenum 大于等于 700 并且 小于 2000 的文档 例:db.comment.find({$and:[{likenum:{$gte:NumberInt( 700 )}},{likenum:{$lt:NumberInt( 2000 )}}]}) OR 写法 $or:[{},{},{}] // 查询评论集合中 userId 为 1003 ,或者点赞数小于 1000 的文档记录 db.comment.find({$or:[{userId: "1003" },{likenum:{$lt: 1000 }}]}) |
2.6 常用命令小结
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 选择切换数据库:use articledb 插入数据:db.comment.insert({bson数据}) 查询所有数据:db.comment.find(); 条件查询数据:db.comment.find({条件}) 查询符合条件的第一条记录:db.comment.findOne({条件}) 查询符合条件的前几条记录:db.comment.find({条件}).limit(条数) 查询符合条件的跳过的记录:db.comment.find({条件}).skip(条数) 修改数据:db.comment.update({条件},{修改后的数据}) 或 db.comment.update({条件},{$set:{要修改部分的字段:数据}) 修改数据并自增某字段值:db.comment.update({条件},{$inc:{自增的字段:步进值}}) 删除数据:db.comment.remove({条件}) 统计查询:db.comment.count({条件}) 模糊查询:db.comment.find({字段名:/正则表达式/}) 条件比较运算:db.comment.find({字段名:{$gt:值}}) 包含查询:db.comment.find({字段名:{$in:[值 1 , 值 2 ]}}) 或 db.comment.find({字段名:{$nin:[值 1 , 值 2 ]}}) 条件连接查询:db.comment.find({$and:[{条件 1 },{条件 2 }]}) 或 db.comment.find({$or:[{条件 1 },{条件 2 }]}) |
3. 文档间的对应关系
-
一对一 (One To One)
-
一对多 (One To Many)
-
多对多 (Many To Many)
1 2 | var user_id = db.users.findOne( {username: "username_here" } )._id db.orders.find( {user_id: user_id} ) |
4. MongoDB 的索引
4.1 概述
索引支持在 MongoDB 中高效地执行查询.如果没有索引, MongoDB 必须执行全集合扫描, 即扫描集合中的每个文档, 以选择与查询语句 匹配的文档.这种扫描全集合的查询效率是非常低的, 特别在处理大量的数据时, 查询可以要花费几十秒甚至几分钟, 这对网站的性能是非常致命的.
如果查询存在适当的索引, MongoDB 可以使用该索引限制必须检查的文档数.
索引是特殊的数据结构, 它以易于遍历的形式存储集合数据集的一小部分.索引存储特定字段或一组字段的值, 按字段值排序.索引项的排 序支持有效的相等匹配和基于范围的查询操作.此外, MongoDB 还可以使用索引中的排序返回排序结果.
MongoDB 使用的是 B Tree, MySQL 使用的是 B+ Tree
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | // create index db.<collection_name>.createIndex({ userid : 1 , username : - 1 }) // retrieve indexes db.<collection_name>.getIndexes() // remove indexes db.<collection_name>.dropIndex(index) // there are 2 ways to remove indexes: // 1. removed based on the index name // 2. removed based on the fields db.<collection_name>.dropIndex( "userid_1_username_-1" ) db.<collection_name>.dropIndex({ userid : 1 , username : - 1 }) // remove all the indexes, will only remove non_id indexes db.<collection_name>.dropIndexes() |
4.2 索引的类型
4.2.1 单字段索引
MongoDB 支持在文档的单个字段上创建用户定义的升序/降序索引, 称为单字段索引 Single Field Index
对于单个字段索引和排序操作, 索引键的排序顺序(即升序或降序)并不重要, 因为 MongoDB 可以在任何方向上遍历索引.
4.2.2 复合索引
MongoDB 还支持多个字段的用户定义索引, 即复合索引 Compound Index
复合索引中列出的字段顺序具有重要意义.例如, 如果复合索引由 { userid: 1, score: -1 }
组成, 则索引首先按 userid
正序排序, 然后 在每个 userid
的值内, 再在按 score
倒序排序.
4.2.3 其他索引
-
地理空间索引 Geospatial Index
-
文本索引 Text Indexes
-
哈希索引 Hashed Indexes
地理空间索引(Geospatial Index)
为了支持对地理空间坐标数据的有效查询, MongoDB 提供了两种特殊的索引: 返回结果时使用平面几何的二维索引和返回结果时使用球面几何的二维球面索引.
文本索引(Text Indexes)
MongoDB 提供了一种文本索引类型, 支持在集合中搜索字符串内容.这些文本索引不存储特定于语言的停止词(例如 “the”, “a”, “or”), 而将集合中的词作为词干, 只存储根词.
哈希索引(Hashed Indexes)
为了支持基于散列的分片, MongoDB 提供了散列索引类型, 它对字段值的散列进行索引.这些索引在其范围内的值分布更加随机, 但只支持相等匹配, 不支持基于范围的查询.
4.3 索引的管理操作
4.3.1 索引的查看
1 | db.collection.getIndexes() |
4.3.2 索引的创建
1 2 | db.collection.createIndex(keys, options) // db.collection.createIndex({item:1}) |
4.3.3 索引的删除
1 2 3 4 5 6 7 8 | # 删除某一个索引 $ db.collection.dropIndex(index) # 删除全部索引 $ db.collection.dropIndexes() # 删除 comment 集合中 userid 字段上的升序索引 $ db.comment.dropIndex({userid: 1 }) |
_id
的字段的索引是无法删除的, 只能删除非 _id
字段的索引
4.4 索引的使用
分析查询性能 (Analyze Query Performance) 通常使用执行计划 (解释计划 - Explain Plan) 来查看查询的情况
1 | $ db.<collection_name>.find( query, options ).explain(options) |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南