📖 第二章:MongoDB数据库概念和特点、数据库操作、集合操作、文档操作、规范及常见问题解决、实际应用示例
文章目录
2.1 数据库概念及MongoDB数据库的特点
数据库就像一个巨大的文件柜,里面有很多抽屉(数据库),每个抽屉里有很多文件夹(集合),文件夹里存放着各种文件(文档)。在MongoDB中,数据库就是用来组织和存储相关数据的容器。
MongoDB数据库的特点:
自动创建
MongoDB有一个非常方便的特性:当你第一次向一个不存在的数据库插入数据时,它会自动创建这个数据库。这就像你第一次往一个新抽屉里放东西时,抽屉会自动出现一样。无需预定义结构
与传统数据库不同,MongoDB不需要预先定义表结构,你可以随时添加新的字段,就像在文件夹里随时添加新的文件一样。
2.2 MongoDB数据库操作指令
use
:指定某个数据库(该数据库不存在,MongoDB将自动创建它)。show dbs
:查看所有数据库。db
:查看当前数据库。db.stats()
:查看数据库统计信息。db.getName()
:查看数据库名称。db.runCommand({dbStats: 1, scale: 1024})
:查看数据库大小,以KB为单位。db.copyDatabase("source_db", "target_db", "localhost:27017")
:复制数据库:source_db
(源数据库)→target_db
(目标数据库)。- 第一个参数 “source_db”:源数据库名称(要复制的数据库)。
- 第二个参数 “target_db”:目标数据库名称(复制后生成的新数据库)。
- 第三个参数 “localhost:27017”:源数据库所在的服务器地址(这里是本地默认端口,若源数据库在远程服务器,需填写 ip:port)。
- 执行后,MongoDB 会将 "source_db “中的所有集合、文档、索引完整复制到"target_db”,两个数据库成为独立副本(后续修改源数据库不会影响目标数据库)。
- 若 “target_db” 已存在,复制会失败,需先删除目标数据库或更换名称。
- 适合快速创建测试环境副本、临时备份小型数据库,不建议用于超大型数据库(可能导致性能下降)。
db.dropDatabase()
:删除当前数据库(包含所有集合),删除后常使用show dbs
来验证删除。- 数据库维护,示例:
// 修复数据库
use myapp
db.repairDatabase()
// 验证数据库
db.runCommand({validate: "users"})
// 压缩数据库
db.runCommand({compact: "users"})
2.3 MongoDB集合操作指令
指定数据库之后,针对集合操作:
show collections
:查看当前数据库的所有集合,或者使用show tables
。db.集合名字.stats()
:查看集合统计。db.集合名字.insertOne()
:向数据库指定名字的集合中插入一条数据(文档),如果没有这个集合的话插入数据的时候会自动创建,其中的db.集合名字
表示当前数据库中的指定名字的那个集合。- 常见的
db.admins.insertOne()
中的users
只是一个常见的集合命名(用于存储普通用户数据)。 - 想存储管理员用户,可以命名集合为
admins
,即使用db.admins.insertOne()
。 - 再比如,存储第三方登录用户,可以命名为
oauth_users
,即使用db.oauth_users.insertOne()
。
- 常见的
db.集合名字.insertMany()
:向数据库指定名字的集合中插入多条数据(文档),没有集合会自动创建。db.集合名字.find()
:查看集合中的数据。db.集合名字.drop()
:删除指定集合名字的集合,.drop()
是删除集合的核心方法,执行后集合的所有数据(文档、索引)会被永久删除,释放磁盘空间。删除集合需注意:- 使用之前必须确定清楚集合所在的数据库并使用
use
切换进去,再删除,否则会默认操作当前数据库,可能导致找不到目标集合。 - 不可逆性:删除集合后,其中的文档和索引无法恢复,建议操作前备份重要数据。
- 权限要求:执行删除操作的用户需具备集合的 drop 权限(通常是数据库的
readWrite
或更高权限)。 - 返回值含义:返回 true 表示删除成功;返回 false 通常是因为集合不存在或无权限。
- 使用之前必须确定清楚集合所在的数据库并使用
db.createCollection("集合名字")
:显式创建集合。db.createCollection(name, options)
:创建带选项的集合。name
:字符串,必选,指定集合名称。options
:对象,可选,配置集合的特殊属性。例如可以指定capped
、size
、max
、autoIndexId
、validator
等参数。MongoDB 中db.createCollection()
方法的所有常用配置参数,汇总如下:
参数名 | 类型 | 描述 | 示例值 |
---|---|---|---|
capped |
布尔值 | 是否创建固定大小集合(容量满后自动覆盖最旧数据) | true |
size |
数值 | 固定集合的最大存储空间(字节),仅当 capped: true 时有效且为必填项 |
10485760 (10MB) |
max |
数值 | 固定集合允许的最大文档数量,仅当 capped: true 时有效(与 size 冲突时,以先达到的条件为准) |
5000 |
autoIndexId |
布尔值 | 是否自动为 _id 字段创建索引(默认值为 true ,不建议关闭,可能影响查询性能) |
false |
validator |
对象 | 定义文档插入/更新时的验证规则(使用 MongoDB 查询操作符),不符合规则的操作将受 validationAction 控制 |
{ $jsonSchema: { required: ["username", "email"] } } |
validationLevel |
字符串 | 指定文档验证的严格程度: - off :不进行任何验证- strict :对所有插入和更新操作进行验证(默认值)- moderate :仅对新插入文档和修改过的现有文档进行验证 |
"moderate" |
validationAction |
字符串 | 指定验证失败时的处理行为: - error :阻止不符合规则的插入/更新操作(默认值)- warn :允许操作,但记录警告日志 |
"warn" |
storageEngine |
对象 | 为集合指定存储引擎配置(需数据库支持多存储引擎),可针对不同引擎设置特定参数(如压缩算法) | { wiredTiger: { configString: "block_compressor=zstd" } } |
collation |
对象 | 定义集合的默认排序规则,包括语言、大小写敏感性、重音符号处理等 | { locale: "zh", strength: 2, caseLevel: false } (中文,忽略大小写) |
timeseries |
对象 | 创建时间序列集合(优化时序数据存储,如监控指标、传感器数据),必选 timeField (存储时间戳的字段名),可选 metaField (元数据字段)和 granularity (数据粒度:seconds /minutes /hours ) |
{ timeField: "timestamp", metaField: "deviceId", granularity: "hours" } |
expireAfterSeconds |
数值 | 文档自动过期时间(秒),需配合日期字段的索引使用(文档会在指定时间后被自动删除) | 86400 (24小时后过期) |
viewOn |
字符串 | 创建视图(基于其他集合的虚拟集合)时,指定源集合的名称,需与 pipeline 配合使用 |
"rawData" |
pipeline |
数组 | 创建视图时的聚合管道,定义视图数据的筛选、转换或计算规则(由聚合操作符组成) | [{ $match: { status: "active" } }, { $project: { name: 1, value: 1 } }] |
参数说明:
- capped: true
- 核心开关:设置为 true 时,集合成为固定大小集合(容量和数量受限,满了会自动覆盖旧数据)。
- 固定集合的特性:
- 容量固定,写入顺序严格按照插入顺序(类似日志文件的 “先进先出”)。
- 不支持删除单个文档(只能清空整个集合),也不允许修改文档大小(避免破坏存储结构)。
- 读取效率极高,适合存储日志、监控数据等按时间顺序写入和查询的场景。
- size: 100000
- 单位:字节(Byte),此处 100000 表示 100KB。
- 作用:限制集合的最大存储空间。当集合占用的磁盘空间达到 100KB 时,新写入的文档会自动覆盖最旧的文档。
- 注意:size 是固定集合(
capped
为true)的必选参数(即使设置了max
,也必须指定size
)。
- max: 1000
- 单位:文档数量,此处表示最多存储 1000 个文档。
- 作用:限制集合能存储的最大文档数量。当文档数量达到 1000 个时,新写入的文档会自动覆盖最旧的文档。
- 优先级:
size
和max
同时设置时,哪个条件先满足就触发覆盖(例如,若 1000 个文档的总大小未达 100KB,则按数量限制;若未到 1000 个文档但总大小达 100KB,则按空间限制)。
- autoIndexId(索引相关)
- 类型:布尔值(默认 true)。
- 作用:是否自动为 _id 字段创建索引。
- 示例:
{ autoIndexId: false }
表示不自动创建 _id 索引(不推荐,可能影响查询效率)。
- validator(验证规则(数据校验))
- 类型:文档(查询表达式)。
- 作用:定义文档插入 / 更新时的验证规则,不符合规则的操作会被拒绝。
- 示例:要求插入的文档必须包含 email 字段且格式符合邮箱规则:
db.createCollection("users", {
validator: {
$and: [
{ email: { $exists: true } }, // 必须存在email字段
{ email: { $regex: /@/ } } // email格式需包含@
]
}
})
- validationLevel
- 类型:字符串(默认 strict)。
- 作用:指定验证严格程度:
- strict:对所有插入 / 更新操作进行验证(默认)。
- moderate:仅对新插入的文档和修改过的现有文档进行验证。
- validationAction
- 类型:字符串(默认 error)。
- 作用:验证失败时的处理方式:
- error:直接拒绝不符合规则的操作(默认)。
- warn:允许操作,但记录警告日志。
- storageEngine
- 类型:文档。
- 作用:为集合指定特定的存储引擎配置(需数据库支持多存储引擎)。
- 示例:为集合指定 WiredTiger 存储引擎的压缩方式:
db.createCollection("largeData", {
storageEngine: {
wiredTiger: {
configString: "block_compressor=zstd" // 使用zstd压缩算法
}
}
})
- collation
- 类型:文档。
- 作用:定义集合的排序规则(如语言、大小写敏感性)。
- 示例:创建一个不区分大小写的集合:
db.createCollection("products", {
collation: {
locale: "en",
strength: 2 // 1=忽略大小写和重音,2=忽略大小写(保留重音)
}
})
- timeseries
- 类型:文档。
- 作用:创建时间序列集合(专门优化用于存储时序数据,如监控指标、传感器数据)。
- 必选子参数:
- timeField:指定存储时间戳的字段名(如 timestamp)。
- 示例:
db.createCollection("sensorData", {
timeseries: {
timeField: "timestamp", // 时间戳字段
metaField: "deviceId", // 元数据字段(如设备ID,用于分组)
granularity: "minutes" // 数据粒度(优化查询性能)
}
})
- expireAfterSeconds
- 类型:数字。
- 作用:为集合中的文档设置自动过期时间(需配合 _id 以外的日期字段使用)。
- 示例:文档在 createdAt 字段指定的时间后 3600 秒(1 小时)自动删除:
db.createCollection("tempData", { expireAfterSeconds: 3600 })
// 需手动为createdAt字段创建索引:
db.tempData.createIndex({ createdAt: 1 })
- viewOn
- 类型:字符串。
- 作用:创建视图(基于其他集合的虚拟集合,不存储实际数据,仅展示源集合经处理后的结果)时,用于指定源集合的名称。必须与
pipeline
参数配合使用,否则无法创建视图。 - 示例:基于
orders
集合创建视图:
db.createCollection("activeOrdersView", {
viewOn: "orders", // 源集合为orders
pipeline: [{ $match: { status: "active" } }] // 配合pipeline定义视图规则
})
- pipeline
- 类型:数组(由聚合操作符组成)。
- 作用:创建视图时,定义对源集合数据的筛选、转换、计算等规则(即聚合管道),视图将展示经过管道处理后的结果。需与
viewOn
参数配合使用。 - 示例:从
orders
集合中筛选出状态为“completed”的文档,并只展示orderId
和totalAmount
字段:
db.createCollection("completedOrdersView", {
viewOn: "orders",
pipeline: [
{ $match: { status: "completed" } }, // 筛选状态为completed的文档
{ $project: { orderId: 1, totalAmount: 1, _id: 0 } } // 只保留指定字段
]
})
// 视图completedOrdersView将只显示符合条件的文档及指定字段
(续上述集合操作)更新集合名
在 MongoDB 中,重命名集合主要有两种方法:方法一:通过
db.adminCommand()
执行renameCollection
命令- 适用场景:适用于所有 MongoDB 环境(包括 shell 命令行、驱动程序调用),是通用性较强的官方推荐方式。
- 语法格式:
db.adminCommand({ renameCollection: "<旧集合完整名称>", // 必选,格式为 "数据库名.集合名"(需包含所属数据库) to: "<新集合完整名称>", // 必选,格式同上(新集合的数据库可以与旧集合不同,实现跨库移动) dropTarget: <布尔值> // 可选,默认 false;若新集合已存在,true 表示删除旧的新集合后重命名,false 表示不删除(此时会报错) })
- 示例:
- 将
test
数据库中的users
集合重命名为customers
(同库重命名):db.adminCommand({ renameCollection: "test.users", to: "test.customers" })
- 将
test
数据库的users
移动到newdb
数据库并命名为clients
(跨库移动+重命名):db.adminCommand({ renameCollection: "test.users", to: "newdb.clients" })
- 将
方法二:直接调用数据库对象的
renameCollection()
方法- 适用场景:主要用于 MongoDB shell 环境或支持该方法的驱动程序,操作更简洁,但功能相对单一。
- 语法格式:
// 先切换到集合所属的数据库 use 数据库名 // 调用 renameCollection 方法 db.renameCollection("<旧集合名>", "<新集合名>", <dropTarget>)
- 参数说明:
<旧集合名>
:必选,仅需集合名(无需带数据库,因已通过use
切换到目标库)。<新集合名>
:必选,仅需集合名(新集合会被创建在当前数据库中,不支持跨库操作)。<dropTarget>
:可选,布尔值,默认 false(作用同方法一)。
- 参数说明:
- 示例:
将test
数据库中的users
集合重命名为customers
(同库操作):use test // 切换到 test 数据库 db.renameCollection("users", "customers") // 重命名当前库下的集合
两种方法的核心区别
对比项 方法一( db.adminCommand
)方法二( db.renameCollection
)跨库操作 支持(可将集合移动到其他数据库) 不支持(只能在当前数据库内重命名) 集合名称格式 需带数据库名(如 test.users
)仅需集合名(依赖 use
切换的数据库)适用环境 所有环境(shell、驱动程序) 主要用于 shell 或特定驱动 - 更新集合名,需要注意:
- 重命名操作会临时锁定源集合,期间无法写入数据,建议在低峰期执行。
- 若新集合已存在且
dropTarget
为false
,两种方法都会报错(需手动删除旧的新集合或设置dropTarget: true
)。 - 【重要】执行用户需具备
renameCollection
权限(通常为数据库管理员权限)。
2.4 MongoDB文档操作指令
文档的数据结构和 JSON 基本一样。所有存储在集合中的数据都是 BSON 格式(Binary JSON 的简称)。
指定数据库和集合之后,针对文档(数据记录)的操作:
db.集合名字.insertOne(document)
:向集合中插入单条文档。document
:必选,要插入的文档(JSON格式对象),字段可自定义。- 示例:向
users
集合插入一条用户文档db.users.insertOne({ username: "lisi", age: 30, status: "active" })
- 插入成功后,MongoDB会自动为文档添加
_id
字段(唯一标识,若未指定则自动生成ObjectId)。
db.集合名字.insertMany(documents, options)
:向集合中插入多条文档。documents
:必选,文档数组(包含多个JSON对象)。options
:可选,配置项,如ordered: true
(默认,按顺序插入,失败则终止)、ordered: false
(并行插入以提高插入性能,忽略单条失败)。- 示例:批量插入用户文档
db.users.insertMany([ { username: "wangwu", age: 28, status: "inactive" }, { username: "zhaoliu", age: 35, status: "active" } ])
db.集合名字.find(query, projection)
:查询集合中的文档,返回所有符合条件的结果(默认返回全部字段)。query
:可选,查询条件(JSON对象),不指定则返回所有文档。projection
:可选,投影设置(指定返回哪些字段),1
表示显示,0
表示隐藏(_id
默认显示,需显式设置0
隐藏)。- 示例:
- 查询所有状态为
active
的用户:db.users.find({ status: "active" })
- 查询年龄大于25的用户,只返回
username
和age
字段:db.users.find({ age: { $gt: 25 } }, { username: 1, age: 1, _id: 0 })
- 查询所有状态为
- 常用查询操作符:
$eq
(等于)、$gt
(大于)、$lt
(小于)、$in
(在数组中)、$and
(逻辑与)等。
db.集合名字.findOne(query, projection)
:查询符合条件的第一条文档(返回单个文档,而非数组)。- 参数同
find()
,适用于获取唯一结果(如按_id
查询)。 - 示例:查询
username
为lisi
的用户db.users.findOne({ username: "lisi" })
- 参数同
db.集合名字.updateOne(filter, update, options)
:更新符合条件的第一条文档。filter
:必选,筛选条件(同查询的query
)。update
:必选,更新操作(需使用更新操作符,如$set
、$inc
等)。options
:可选,如upsert: true
(若文档不存在则插入新文档)。- 示例:将
lisi
的年龄更新为31db.users.updateOne( { username: "lisi" }, // 筛选条件 { $set: { age: 31 } } // 更新操作:设置age字段 )
- 常用更新操作符:
$set
(修改字段值)、$inc
(数值增减)、$push
(向数组添加元素)、$unset
(删除字段)等。
db.集合名字.updateMany(filter, update, options)
:更新所有符合条件的文档。- 参数同
updateOne()
,但会批量更新匹配的所有文档。 - 示例:将所有
status
为inactive
的用户年龄增加1db.users.updateMany( { status: "inactive" }, { $inc: { age: 1 } } // 年龄+1 )
- 参数同
db.集合名字.replaceOne(filter, replacement, options)
:替换符合条件的第一条文档(用新文档完全覆盖旧文档,保留_id
)。replacement
:必选,用于替换的新文档(不含更新操作符)。- 示例:替换
wangwu
的文档内容db.users.replaceOne( { username: "wangwu" }, { username: "wangwu", age: 29, status: "active", hobby: "reading" } )
db.集合名字.deleteOne(filter)
:删除符合条件的第一条文档。filter
:必选,筛选条件(同查询)。- 示例:删除
username
为zhaoliu
的文档db.users.deleteOne({ username: "zhaoliu" })
db.集合名字.deleteMany(filter)
:删除所有符合条件的文档。- 示例:删除所有
status
为inactive
的文档db.users.deleteMany({ status: "inactive" })
- 示例:删除所有
db.集合名字.countDocuments(query)
:统计符合条件的文档数量。query
:可选,筛选条件,不指定则统计集合总文档数。- 示例:统计状态为
active
的用户数量db.users.countDocuments({ status: "active" })
db.集合名字.distinct(field, query)
:查询指定字段的去重值,返回数组。field
:必选,要去重的字段名。query
:可选,筛选条件。- 示例:查询所有用户的
status
去重后的值db.users.distinct("status") // 返回 ["active", "inactive"]
db.集合名字.aggregate(pipeline)
:聚合查询(复杂数据处理,如分组、统计、关联等)。pipeline
:必选,聚合管道数组(由多个聚合阶段组成)。- 示例:按
status
分组统计用户数量db.users.aggregate([ { $group: { _id: "$status", count: { $sum: 1 } } } ])
- 常用聚合阶段:
$match
(筛选)、$group
(分组)、$sort
(排序)、$limit
(限制数量)等。
2.5 规范及常见问题解决
数据库命名规范
1、命名规则
// 正确的数据库名
use myapp // 小写字母
use user_management // 下划线分隔
use test123 // 包含数字
// 错误的数据库名
use MyApp // 避免大写字母
use user-management // 避免连字符
use 123test // 不能以数字开头
use my app // 不能包含空格
2、最佳实践
// 使用有意义的名称
use ecommerce_db // 电商数据库
use blog_system // 博客系统数据库
use school_management // 学校管理数据库
// 避免使用特殊字符
// 避免使用MongoDB保留字
// 使用小写字母和下划线
故障排除
问题1:数据库不显示
// 原因:数据库为空
use myapp
db.users.insertOne({"test": "data"})
show dbs
// 现在应该能看到myapp
问题2:无法创建数据库
// 检查权限
use admin
db.auth("admin", "password")
// 检查磁盘空间
db.stats()
问题3:集合创建失败
// 检查集合名是否合法
// 集合名不能以"system."开头
db.createCollection("mycollection") // 正确
db.createCollection("system.test") // 错误
2.6 实际应用示例
第一步:连接到MongoDB
启动MongoDB Shell
# 连接到本地MongoDB
mongosh
# 连接到远程MongoDB
mongosh --host 192.168.1.100 --port 27017
# 连接到特定数据库
mongosh --db myapp
基本连接操作
// 查看当前数据库
db
// 查看所有数据库
show dbs
// 查看当前数据库的所有集合
show collections
第二步:创建第一个数据库
使用use命令创建数据库
// 切换到(或创建)名为"myapp"的数据库
use myapp
// 查看当前数据库
db
// 输出: myapp
// 查看所有数据库
show dbs
// 注意:此时myapp数据库可能还不会显示
为什么新创建的数据库不显示?
// 刚创建的数据库不会立即显示在列表中
use myapp
show dbs
// 输出可能不包含myapp
// 需要插入数据后才会显示
db.users.insertOne({"name": "张三", "age": 25})
show dbs
// 现在myapp会显示在列表中
第三步:向数据库插入数据
插入第一条数据
// 切换到myapp数据库
use myapp
// 插入一条用户数据
db.users.insertOne({
"name": "张三",
"email": "zhangsan@example.com",
"age": 25,
"created_at": new Date()
})
// 查看插入结果
// 输出: { "acknowledged" : true, "insertedId" : ObjectId("...") }
插入多条数据
// 插入多条用户数据
db.users.insertMany([
{
"name": "李四",
"email": "lisi@example.com",
"age": 30,
"created_at": new Date()
},
{
"name": "王五",
"email": "wangwu@example.com",
"age": 28,
"created_at": new Date()
}
])
验证数据库创建
// 查看所有数据库
show dbs
// 现在应该能看到myapp数据库
// 查看当前数据库的集合
show collections
// 应该能看到users集合
// 查看集合中的数据
db.users.find()
第四步:创建集合
方法一:自动创建集合
// 插入数据时自动创建集合
use myapp
db.products.insertOne({
"name": "笔记本电脑",
"price": 5999,
"category": "电子产品"
})
方法二:显式创建集合
// 显式创建集合
use myapp
db.createCollection("orders")
// 查看所有集合
show collections
// 输出: orders, products, users
创建带选项的集合
// 创建固定大小的集合(适合存储日志)
db.createCollection("logs", {
capped: true,
size: 100000, // 100KB
max: 1000 // 最多1000个文档
})
// 创建带验证规则的集合
db.createCollection("employees", {
validator: {
$jsonSchema: {
bsonType: "object",
required: ["name", "email"],
properties: {
name: {
bsonType: "string",
description: "姓名必须是字符串"
},
email: {
bsonType: "string",
pattern: "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$",
description: "邮箱格式不正确"
}
}
}
}
})
第五步:数据库操作
查看数据库信息
// 查看当前数据库
db
// 查看数据库统计信息
db.stats()
// 查看数据库名称
db.getName()
删除数据库
// 切换到要删除的数据库
use testdb
// 删除当前数据库
db.dropDatabase()
// 验证删除
show dbs
// testdb应该不再显示
复制数据库
// 复制数据库(需要管理员权限)
use admin
db.copyDatabase("source_db", "target_db", "localhost:27017")
实际应用场景:电商网站数据库
// 创建电商数据库
use ecommerce
// 创建用户集合
db.users.insertOne({
"username": "customer1",
"email": "customer1@example.com",
"profile": {
"firstName": "张",
"lastName": "三",
"phone": "13800138000"
},
"addresses": [
{
"type": "home",
"street": "中关村大街1号",
"city": "北京",
"zipCode": "100080"
}
],
"created_at": new Date()
})
// 创建产品集合
db.products.insertOne({
"name": "iPhone 15",
"category": "手机",
"brand": "Apple",
"price": 6999,
"stock": 100,
"specifications": {
"color": "黑色",
"storage": "128GB",
"screen": "6.1英寸"
},
"created_at": new Date()
})
// 创建订单集合
db.orders.insertOne({
"orderId": "ORD001",
"userId": "customer1",
"items": [
{
"productId": "iPhone 15",
"quantity": 1,
"price": 6999
}
],
"total": 6999,
"status": "pending",
"created_at": new Date()
})