数据库实验-MongoDB安装和操作

数据库实验-MongoDB安装和操作

一、实验环境与 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命令行

image

1.5 服务管理常用命令

命令 功能
docker start mongodb8 启动服务
docker stop mongodb8 停止服务
docker restart mongodb8 重启服务
docker ps 查看运行状态
docker exec -it mongodb8 mongosh 进入容器终端连接数据库

1.6 验证安装结果

# 查看镜像安装
docker images

image

1.7 首次连接

docker exec -it mongodb8 mongosh

image

二、MongoDB 安全配置

本实验使用 Docker 方式部署,无需执行原文档以下操作:

  1. 备份 mongod.conf 配置文件
  2. 修改 net 绑定 IP
  3. 手动登录 admin 创建管理员用户
  4. 手动开启认证配置并重启服务

说明
Docker 启动时已通过环境变量自动创建管理员账号、自动开启身份认证、默认本地端口绑定,已完成全部安全配置,可直接使用认证方式登录,后续所有数据库操作命令与原实验完全一致。

三、实验数据库与样例数据准备

3.1 创建数据库与集合

use schoolDB创建数据库
db.createCollection("students")创建学生集合,类似表
db.createCollection("orders")
show collections查看当前数据库的所有集合

image

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()查询所有学生,美化输出

image

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()
}
])

image
image

解释

  1. insertMany([{} , {}]):一次性插入多条文档,中括号代表数组。
  2. 多条文档结构可以不完全一致,体现 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()

image

解释

  1. 先定义数组:专业、城市、爱好列表,作为数据源。
  2. for 循环从5到64批量生成60条学生数据。
  3. i % 数组长度:循环随机取用数组里的内容。
  4. 三元表达式:偶数为女,奇数为男。
  5. 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()

image

解释

  1. 向订单集合批量插入订单记录。
  2. status 区分已支付PAID、未支付UNPAID,用于后续聚合统计。
  3. new Date("指定日期"):自定义插入固定日期。

四、基础查询操作

4.1 查询全部学生

db.students.find().pretty()

解释
无条件查询,查出集合内的所有文档,pretty() 格式化输出,方便阅读。

4.2 条件查询

// 查询软件工程专业学生
db.students.find({ major: "软件工程" })
![image](https://img2024.cnblogs.com/blog/3798225/202606/3798225-20260604084828979-228041849.png)// 查询成绩大于85分
db.students.find({ score: { $gt: 85 } })
![image](https://img2024.cnblogs.com/blog/3798225/202606/3798225-20260604085020390-694123437.png)// 成绩80~90之间
db.students.find({ score: { $gte: 80, $lte: 90 } })
![image](https://img2024.cnblogs.com/blog/3798225/202606/3798225-20260604085215807-1274806057.png)// 城市在武汉、长沙的学生
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)

image

解释

  1. 投影:控制返回哪些字段,id会默认展示,_id:0 不显示默认主键。
  2. sort({字段:-1}) 降序,1 升序。
  3. limit(n):限制返回条数。
  4. skip(n):跳过前n条,和 limit 搭配做分页。

4.5 数组查询

// 爱好包含编程的学生
db.students.find({ hobbies: "编程" })// 同时包含编程、篮球
db.students.find({ hobbies: { $all: ["编程", "篮球"] } })// 爱好数组长度为2
db.students.find({ hobbies: { $size: 2 } })

image
解释

  • 普通匹配数组字段:只要包含该元素就命中。
  • $all:数组必须同时包含所有指定元素。
  • $size:匹配数组元素个数。

五、更新与删除操作

5.1 更新操作

// 更新单条:修改S2026001成绩为90
db.students.updateOne({ studentNo: "S2026001" }, { $set: { score: 90 } })
未修改成功:
![image](https://img2024.cnblogs.com/blog/3798225/202606/3798225-20260604090841638-217077899.png)
![image](https://img2024.cnblogs.com/blog/3798225/202606/3798225-20260604091719415-405850376.png)// 批量更新:软件工程所有学生成绩加3分
db.students.updateMany({ major: "软件工程" }, { $inc: { score: 3 } })// 批量新增字段status
db.students.updateMany({}, { $set: { status: "active" } })// 删除status字段
db.students.updateMany({}, { $unset: { status: "" } })

image
解释

  • updateOne:只更新匹配到的第一条
  • updateMany:更新所有匹配文档
  • $set:修改或新增字段
  • $inc:数值增减
  • $unset:删除某个字段

5.2 删除操作

// 删除单条指定学号
db.students.deleteOne({ studentNo: "TEMP001" })// 批量删除60分以下学生
db.students.deleteMany({ score: { $lt: 60 } })

image

解释

  • deleteOne:删第一条匹配数据
  • deleteMany:删除所有满足条件的数据
  • $lt:小于

六、聚合操作

// 按专业统计人数并降序
db.students.aggregate([
{ $group: { _id: "$major", studentCount: { $sum: 1 } } },
{ $sort: { studentCount: -1 } }
])
![image](https://img2024.cnblogs.com/blog/3798225/202606/3798225-20260604092047597-78652645.png)// 按专业统计平均分、最高分、最低分
db.students.aggregate([
{ $group: {
_id: "$major",
avgScore: { $avg: "$score" },
maxScore: { $max: "$score" },
minScore: { $min: "$score" }
}
},
{ $sort: { avgScore: -1 } }
])
![image](https://img2024.cnblogs.com/blog/3798225/202606/3798225-20260604092021793-228069683.png)// 只统计已支付订单,按分类汇总金额
db.orders.aggregate([
{ $match: { status: "PAID" } },
{ $group: { _id: "$category", totalAmount: { $sum: "$amount" } } }
])

image

解释

  1. aggregate 聚合管道,多步处理数据。
  2. $match:先过滤数据,只保留符合条件文档。
  3. $group:按指定字段分组,做求和、平均、最大、最小统计。
  4. $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")

image

解释

  1. 索引作用:加快查询速度,类似书的目录。
  2. 复合索引:多个字段联合建索引,适合多条件查询。
  3. getIndexes():查看已创建索引。
  4. 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 文件,适合迁移单张表。
image

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,做备份恢复单表。
image

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

image

九、命令编写题

假设集合名为:students

1. 查询成绩大于85且专业为“计算机科学与技术”的学生,只显示studentNo、name、major、score

db.students.find({major: "计算机科学与技术",score: { $gt: 85 }},{_id: 0,studentNo: 1,name: 1,major: 1,score: 1}
)

image

2. 将“软件工程”专业学生成绩增加5分,并添加remark字段

db.students.updateMany({ major: "软件工程" },{$inc: { score: 5 },$set: { remark: "成绩加5分" }}
)

image

3. 查询包含“编程”的学生,按成绩降序显示姓名、专业、成绩

说明:匹配name字段包含“编程”

db.students.find({ hobbies: "编程" },{ _id: 0, name: 1, major: 1, score: 1 }
).sort({ score: -1 })

image

4. 按城市统计学生人数并降序排列

假设城市字段为 city

db.students.aggregate([{$group: {_id: "$city",count: { $sum: 1 }}},{ $sort: { count: -1 } }
])

image

5. 为major和score创建复合索引,并用explain分析查询计划

创建复合索引

db.students.createIndex({ major: 1, score: -1 })

explain分析示例(随便一条匹配索引的查询)

db.students.find({ major: "计算机科学与技术", score: { $gt: 60 } }).explain()

image
image


十、巩固练习题(课后作业)

10.1 概念题

1. MongoDB中数据库、集合、文档三者之间是什么关系?

  1. 数据库(Database):最高层级容器,一个MongoDB服务可包含多个数据库;
  2. 集合(Collection):数据库下的子容器,类似关系型数据库的,一个库可有多张集合;
  3. 文档(Document):集合里最小数据单元,BSON格式,类似关系库的行/记录

层级关系:数据库 → 多个集合 → 每个集合包含多个文档

2. MongoDB 中 _id 字段有什么作用?

  1. 文档唯一主键,集合内不可重复,默认自动生成;
  2. 作为文档唯一标识,用于区分、查找、关联文档;
  3. 若插入文档不手动指定_id,MongoDB自动生成ObjectId
  4. 索引默认以_id建立,保证文档唯一性。

3. JSON 与 BSON 有什么区别?

  1. BSON是JSON的二进制扩展格式,MongoDB底层存储使用BSON;
  2. 数据类型:
    • JSON仅支持:字符串、数字、布尔、数组、对象、null;
    • BSON额外支持:日期、二进制、ObjectId、正则、高精度小数、长整型等Mongo专用类型;
  3. 性能:BSON二进制解析更快、序列化/反序列化效率更高;
  4. 大小:BSON会携带类型标记,体积略大于同等JSON文本;
  5. 用途:JSON用于网络传输/人类阅读;BSON用于Mongo本地存储、内部交互。

4. MongoDB的“模式自由”有什么优点?可能带来什么问题?

优点

  1. 无需提前定义表结构,新增字段无需改表,迭代灵活;
  2. 同一集合中文档结构可以不一致,适配多变业务数据;
  3. 快速原型开发,需求变更不用维护复杂DDL语句。

问题

  1. 无强制约束,容易出现字段命名、类型混乱;
  2. 应用层必须自行做数据校验,增加代码工作量;
  3. 复杂联表查询不如关系型数据库友好;
  4. 缺乏外键、事务强约束,数据一致性需要业务代码保障;
  5. 团队协作时无统一规范易产生数据脏数据。

5. 比较 mongoexport/mongoimport 与 mongodump/mongorestore 的区别

  1. 格式不同
    • mongoexport/mongoimport:导出导入JSON/CSV文本格式,可读性高;
    • mongodump/mongorestore:导出BSON二进制文件,Mongo原生存储格式;
  2. 适用场景
    • mongoexport:少量数据导出、给第三方阅读、单集合导出;
    • mongodump:整库/批量备份、完整迁移、大数据量备份;
  3. 性能
    • mongodump二进制读写更快,适合海量数据;
    • mongoexport文本转换开销大,大数据效率低;
  4. 功能范围
    • mongoexport仅支持单集合操作;
    • mongodump支持整库、多集合、索引一并备份恢复;
  5. 完整性
    • mongodump会保留索引、数据类型、ObjectId等原生特性;
    • mongoexport导出JSON会丢失部分BSON特有类型(如日期、二进制会被转成字符串)。