一、实验环境与 MongoDB 8.0 安装(Docker 方式)
1.1 实验环境
操作系统:任意支持 Docker 的系统(Windows / macOS / Linux)
数据库版本:MongoDB Community Edition 8.0
连接工具:mongosh
1.2 安装依赖
无需安装 apt 依赖、无需导入 GPG 密钥、无需配置软件源,直接使用 Docker 部署。
1.3 拉取 MongoDB 8.0 镜像
docker pull mongo:8.0
1.4 启动 MongoDB 8.0 容器
首次启动:创建新的容器并运行
docker run --name mongodb -p 27017:27017 -d mongo:latest
再次启动:
docker start mongodb 启动容器
docker exec -it mongodb mongosh进入MongoDB命令行

1.5 服务管理常用命令
| 命令 | 功能 |
|---|---|
| docker start mongodb8 | 启动服务 |
| docker stop mongodb8 | 停止服务 |
| docker restart mongodb8 | 重启服务 |
| docker ps | 查看运行状态 |
| docker exec -it mongodb8 mongosh | 进入容器终端连接数据库 |
1.6 验证安装结果
# 查看镜像安装
docker images

1.7 首次连接
docker exec -it mongodb8 mongosh

二、MongoDB 安全配置
本实验使用 Docker 方式部署,无需执行原文档以下操作:
- 备份 mongod.conf 配置文件
- 修改 net 绑定 IP
- 手动登录 admin 创建管理员用户
- 手动开启认证配置并重启服务
说明:
Docker 启动时已通过环境变量自动创建管理员账号、自动开启身份认证、默认本地端口绑定,已完成全部安全配置,可直接使用认证方式登录,后续所有数据库操作命令与原实验完全一致。
三、实验数据库与样例数据准备
3.1 创建数据库与集合
use schoolDB创建数据库
db.createCollection("students")创建学生集合,类似表
db.createCollection("orders")
show collections查看当前数据库的所有集合

3.2 插入单条学生数据
db.students.insertOne(插入文档数据,格式为{}
{
studentNo: "S2026001",
name: "张三",
gender: "男",
age: 20,
major: "计算机科学与技术",
className: "计科2301",
score: 86,
city: "武汉",
hobbies: ["编程", "篮球"],数组类型,可以有多个,格式为[]
createdAt: new Date()插入当前系统时间
})
db.students.find().pretty()查询所有学生,美化输出

3.3 批量插入学生数据
db.students.insertMany(批量插入A,B,C[
{
studentNo: "S2026002",
name: "李四",
gender: "女",
age: 21,
major: "软件工程",
className: "软工2301",
score: 92,
city: "长沙",
hobbies: ["阅读", "绘画"],
createdAt: new Date()
},
{
studentNo: "S2026003",
name: "王五",
gender: "男",
age: 19,
major: "数据科学与大数据技术",
className: "大数据2301",
score: 78,
city: "郑州",
hobbies: ["跑步", "音乐"],
createdAt: new Date()
},
{
studentNo: "S2026004",
name: "赵六",
gender: "女",
age: 22,
major: "人工智能",
className: "智能2301",
score: 88,
city: "武汉",
hobbies: ["编程", "电影"],
createdAt: new Date()
}
])


解释
insertMany([{} , {}]):一次性插入多条文档,中括号代表数组。- 多条文档结构可以不完全一致,体现 MongoDB 模式自由特点。
3.4 脚本循环生成批量学生数据
var majors = ["计算机科学与技术", "软件工程", "数据科学与大数据技术", "人工智能", "网络工程"];
var cities = ["武汉", "长沙", "郑州", "南昌", "合肥"];
var hobbiesList = ["编程", "篮球", "阅读", "音乐", "跑步", "电影"];
for (var i = 5; i <= 64; i++) {
db.students.insertOne({
studentNo: "S" + String(2026000 + i),
name: "学生" + i,
gender: i % 2 === 0 ? "女" : "男",
age: 18 + (i % 5),
major: majors[i % majors.length],
className: "班级" + ((i % 4) + 1),
score: 50 + (i * 7) % 51,
city: cities[i % cities.length],
hobbies: [hobbiesList[i % hobbiesList.length], hobbiesList[(i + 2) % hobbiesList.length]],
createdAt: new Date()
});
}
db.students.countDocuments()

解释
- 先定义数组:专业、城市、爱好列表,作为数据源。
for循环从5到64批量生成60条学生数据。i % 数组长度:循环随机取用数组里的内容。三元表达式:偶数为女,奇数为男。countDocuments():统计集合总文档数量。
3.5 插入订单数据
db.orders.insertMany([
{ orderNo: "O2026001", customer: "张三", category: "图书", amount: 120, status: "PAID", orderDate: new Date("2026-03-01") },
{ orderNo: "O2026002", customer: "李四", category: "电子产品", amount: 560, status: "PAID", orderDate: new Date("2026-03-02") },
{ orderNo: "O2026003", customer: "王五", category: "图书", amount: 80, status: "UNPAID", orderDate: new Date("2026-03-03") },
{ orderNo: "O2026004", customer: "赵六", category: "生活用品", amount: 230, status: "PAID", orderDate: new Date("2026-03-04") },
{ orderNo: "O2026005", customer: "张三", category: "电子产品", amount: 880, status: "PAID", orderDate: new Date("2026-03-05") },
{ orderNo: "O2026006", customer: "李四", category: "图书", amount: 150, status: "PAID", orderDate: new Date("2026-03-06") }
])
db.orders.countDocuments()

解释
- 向订单集合批量插入订单记录。
status区分已支付PAID、未支付UNPAID,用于后续聚合统计。new Date("指定日期"):自定义插入固定日期。
四、基础查询操作
4.1 查询全部学生
db.students.find().pretty()
解释
无条件查询,查出集合内的所有文档,pretty() 格式化输出,方便阅读。
4.2 条件查询
// 查询软件工程专业学生
db.students.find({ major: "软件工程" })
// 查询成绩大于85分
db.students.find({ score: { $gt: 85 } })
// 成绩80~90之间
db.students.find({ score: { $gte: 80, $lte: 90 } })
// 城市在武汉、长沙的学生
db.students.find({ city: { $in: ["武汉", "长沙"] } })
解释
$gt:大于$gte:大于等于$lte:小于等于$in:在指定数组范围内匹配
4.3 逻辑查询
// 且条件:软件工程 并且 成绩大于80
db.students.find({ $and: [{ major: "软件工程" }, { score: { $gt: 80 } }] })// 或条件:人工智能专业 或者 武汉城市
db.students.find({ $or: [{ major: "人工智能" }, { city: "武汉" }] })
解释
$and:同时满足多个条件$or:满足任意一个条件即可
4.4 投影、排序、分页
// 投影:只显示指定字段,隐藏 _id
db.students.find({}, { _id: 0, studentNo: 1, name: 1, major: 1, score: 1 })// 按成绩降序
db.students.find({}, { _id: 0, name: 1, score: 1 }).sort({ score: -1 })// 分页:取前5条
db.students.find({}, { _id: 0, name: 1, score: 1 }).sort({ score: -1 }).limit(5)// 跳过5条,再取5条,实现第二页
db.students.find({}, { _id: 0, name: 1, score: 1 }).sort({ score: -1 }).skip(5).limit(5)

解释
- 投影:控制返回哪些字段,id会默认展示,
_id:0不显示默认主键。 sort({字段:-1})降序,1升序。limit(n):限制返回条数。skip(n):跳过前n条,和 limit 搭配做分页。
4.5 数组查询
// 爱好包含编程的学生
db.students.find({ hobbies: "编程" })// 同时包含编程、篮球
db.students.find({ hobbies: { $all: ["编程", "篮球"] } })// 爱好数组长度为2
db.students.find({ hobbies: { $size: 2 } })

解释
- 普通匹配数组字段:只要包含该元素就命中。
$all:数组必须同时包含所有指定元素。$size:匹配数组元素个数。
五、更新与删除操作
5.1 更新操作
// 更新单条:修改S2026001成绩为90
db.students.updateOne({ studentNo: "S2026001" }, { $set: { score: 90 } })
未修改成功:

// 批量更新:软件工程所有学生成绩加3分
db.students.updateMany({ major: "软件工程" }, { $inc: { score: 3 } })// 批量新增字段status
db.students.updateMany({}, { $set: { status: "active" } })// 删除status字段
db.students.updateMany({}, { $unset: { status: "" } })

解释
updateOne:只更新匹配到的第一条updateMany:更新所有匹配文档$set:修改或新增字段$inc:数值增减$unset:删除某个字段
5.2 删除操作
// 删除单条指定学号
db.students.deleteOne({ studentNo: "TEMP001" })// 批量删除60分以下学生
db.students.deleteMany({ score: { $lt: 60 } })

解释
deleteOne:删第一条匹配数据deleteMany:删除所有满足条件的数据$lt:小于
六、聚合操作
// 按专业统计人数并降序
db.students.aggregate([
{ $group: { _id: "$major", studentCount: { $sum: 1 } } },
{ $sort: { studentCount: -1 } }
])
// 按专业统计平均分、最高分、最低分
db.students.aggregate([
{ $group: {
_id: "$major",
avgScore: { $avg: "$score" },
maxScore: { $max: "$score" },
minScore: { $min: "$score" }
}
},
{ $sort: { avgScore: -1 } }
])
// 只统计已支付订单,按分类汇总金额
db.orders.aggregate([
{ $match: { status: "PAID" } },
{ $group: { _id: "$category", totalAmount: { $sum: "$amount" } } }
])

解释
aggregate聚合管道,多步处理数据。$match:先过滤数据,只保留符合条件文档。$group:按指定字段分组,做求和、平均、最大、最小统计。$sort:统计完成后再排序。
七、索引操作与执行计划分析
// 单字段索引
db.students.createIndex({ studentNo: 1 })// 复合索引:专业升序、成绩降序
db.students.createIndex({ major: 1, score: -1 })// 城市索引
db.students.createIndex({ city: 1 })// 查看当前集合所有索引
db.students.getIndexes()// 分析单学号查询执行计划
db.students.find({ studentNo: "S2026001" }).explain("executionStats")// 分析专业+成绩条件查询执行计划
db.students.find({ major: "人工智能", score: { $gte: 80 } }).explain("executionStats")

解释
- 索引作用:加快查询速度,类似书的目录。
- 复合索引:多个字段联合建索引,适合多条件查询。
getIndexes():查看已创建索引。explain():查看SQL执行细节,判断有没有用上索引、查询耗时。
八、数据导入、导出、备份与恢复
8.1 导出集合为JSON
在容器内导出数据,复制文件到Windows
docker exec -it mongodb mongoexport --db test --collection students --out /tmp/students.jsondocker cp mongodb:/tmp/students.json C:\Users\佑安\students.json
解释
把 students 集合导出为本地 json 文件,适合迁移单张表。

8.2 导入JSON到新集合
mongoimport --host 127.0.0.1 --port 27017 \
-u mongoAdmin -p 'Mongo@2026StrongPwd' --authenticationDatabase admin \
--db schoolDB --collection students_backup --file ~/students.json
解释
把导出的 json 文件导入到新集合 students_backup,做备份恢复单表。

8.3 整库备份与恢复
# 整库备份(在容器内执行)
docker exec -it mongodb mongodump --db test --out /tmp/backup# 复制备份文件到 Windows
docker cp mongodb:/tmp/backup C:\Users\佑安\backup# 整库恢复(如需恢复时使用)
docker cp C:\Users\佑安\backup mongodb:/tmp/backup_restore
docker exec -it mongodb mongorestore --db test /tmp/backup_restore/test

九、命令编写题
假设集合名为:students
1. 查询成绩大于85且专业为“计算机科学与技术”的学生,只显示studentNo、name、major、score
db.students.find({major: "计算机科学与技术",score: { $gt: 85 }},{_id: 0,studentNo: 1,name: 1,major: 1,score: 1}
)

2. 将“软件工程”专业学生成绩增加5分,并添加remark字段
db.students.updateMany({ major: "软件工程" },{$inc: { score: 5 },$set: { remark: "成绩加5分" }}
)

3. 查询包含“编程”的学生,按成绩降序显示姓名、专业、成绩
说明:匹配name字段包含“编程”
db.students.find({ hobbies: "编程" },{ _id: 0, name: 1, major: 1, score: 1 }
).sort({ score: -1 })

4. 按城市统计学生人数并降序排列
假设城市字段为 city
db.students.aggregate([{$group: {_id: "$city",count: { $sum: 1 }}},{ $sort: { count: -1 } }
])

5. 为major和score创建复合索引,并用explain分析查询计划
创建复合索引
db.students.createIndex({ major: 1, score: -1 })
explain分析示例(随便一条匹配索引的查询)
db.students.find({ major: "计算机科学与技术", score: { $gt: 60 } }).explain()


十、巩固练习题(课后作业)
10.1 概念题
1. MongoDB中数据库、集合、文档三者之间是什么关系?
- 数据库(Database):最高层级容器,一个MongoDB服务可包含多个数据库;
- 集合(Collection):数据库下的子容器,类似关系型数据库的表,一个库可有多张集合;
- 文档(Document):集合里最小数据单元,BSON格式,类似关系库的行/记录;
层级关系:数据库 → 多个集合 → 每个集合包含多个文档
2. MongoDB 中 _id 字段有什么作用?
- 文档唯一主键,集合内不可重复,默认自动生成;
- 作为文档唯一标识,用于区分、查找、关联文档;
- 若插入文档不手动指定
_id,MongoDB自动生成ObjectId; - 索引默认以
_id建立,保证文档唯一性。
3. JSON 与 BSON 有什么区别?
- BSON是JSON的二进制扩展格式,MongoDB底层存储使用BSON;
- 数据类型:
- JSON仅支持:字符串、数字、布尔、数组、对象、null;
- BSON额外支持:日期、二进制、ObjectId、正则、高精度小数、长整型等Mongo专用类型;
- 性能:BSON二进制解析更快、序列化/反序列化效率更高;
- 大小:BSON会携带类型标记,体积略大于同等JSON文本;
- 用途:JSON用于网络传输/人类阅读;BSON用于Mongo本地存储、内部交互。
4. MongoDB的“模式自由”有什么优点?可能带来什么问题?
优点
- 无需提前定义表结构,新增字段无需改表,迭代灵活;
- 同一集合中文档结构可以不一致,适配多变业务数据;
- 快速原型开发,需求变更不用维护复杂DDL语句。
问题
- 无强制约束,容易出现字段命名、类型混乱;
- 应用层必须自行做数据校验,增加代码工作量;
- 复杂联表查询不如关系型数据库友好;
- 缺乏外键、事务强约束,数据一致性需要业务代码保障;
- 团队协作时无统一规范易产生数据脏数据。
5. 比较 mongoexport/mongoimport 与 mongodump/mongorestore 的区别
- 格式不同
mongoexport/mongoimport:导出导入JSON/CSV文本格式,可读性高;mongodump/mongorestore:导出BSON二进制文件,Mongo原生存储格式;
- 适用场景
- mongoexport:少量数据导出、给第三方阅读、单集合导出;
- mongodump:整库/批量备份、完整迁移、大数据量备份;
- 性能
- mongodump二进制读写更快,适合海量数据;
- mongoexport文本转换开销大,大数据效率低;
- 功能范围
- mongoexport仅支持单集合操作;
- mongodump支持整库、多集合、索引一并备份恢复;
- 完整性
- mongodump会保留索引、数据类型、ObjectId等原生特性;
- mongoexport导出JSON会丢失部分BSON特有类型(如日期、二进制会被转成字符串)。