MongoDB 入门教程:从零开始学
数据库是现代应用程序不可或缺的基石,而 MongoDB 作为最流行的 NoSQL 数据库之一,因其灵活的数据模型、易于扩展的特性以及高性能的表现,受到了广泛的关注和应用。如果你是数据库领域的新手,或者习惯了传统的关系型数据库(如 MySQL, PostgreSQL),想要了解并学习 NoSQL 数据库,那么 MongoDB 是一个非常好的起点。
本篇文章将带领你从零开始,一步步了解 MongoDB 的基本概念、安装配置、核心操作以及一些进阶知识的入门。我们将重点关注 MongoDB Shell 的使用,因为它是学习和操作 MongoDB 最直接的方式。
第一章:初识 MongoDB – 为什么选择它?
1.1 什么是 NoSQL?
在深入了解 MongoDB 之前,我们需要先理解 NoSQL 的概念。NoSQL 是 “Not Only SQL” 的缩写,泛指非关系型数据库。与传统的关系型数据库(RDBMS)不同,NoSQL 数据库不强制使用固定的表结构(Schema),通常没有严格的关系模型,并且擅长处理海量数据和高并发访问。
NoSQL 数据库有多种类型,常见的包括:
- 键值对数据库 (Key-Value Stores):如 Redis, Memcached,数据存储为简单的键值对。
- 文档数据库 (Document Databases):如 MongoDB, Couchbase,数据以文档形式存储,文档通常是 JSON、BSON 或 XML 格式。
- 列族数据库 (Column-Family Stores):如 Cassandra, HBase,数据存储在列族中,适合处理分布式、大规模数据。
- 图数据库 (Graph Databases):如 Neo4j, OrientDB,数据存储为节点和边,适合处理复杂的关系网络。
MongoDB 就属于文档数据库。
1.2 什么是 MongoDB?
MongoDB 是一个开源的、高性能、高可用性、易扩展的文档数据库。它使用类似 JSON 的 BSON(Binary JSON)格式存储数据,这意味着它的数据结构非常灵活,不需要预先定义严格的模式。
1.3 MongoDB 的核心特点与优势
- 文档模型 (Document Model):数据以文档(Document)的形式存储,一个文档可以包含嵌套的文档、数组等复杂结构。这使得数据结构更接近于面向对象的编程语言,易于开发和理解。
- 无模式/灵活模式 (Schema-less/Flexible Schema):同一个集合(Collection,类似于关系型数据库的表)中的文档可以有不同的结构。这对于快速迭代、需求经常变化的项目非常有利。
- 高性能 (High Performance):支持内嵌文档和数组,可以减少连接(Join)操作,提高查询性能。
- 高可用性 (High Availability):通过复制集(Replica Set)机制提供数据冗余和自动故障转移。
- 易扩展性 (Scalability):支持分片(Sharding)机制,可以将大量数据分布到多个服务器上,实现横向扩展。
- 丰富的查询语言 (Rich Query Language):支持丰富的查询表达式,包括范围查询、正则表达式查询等,以及强大的聚合框架(Aggregation Framework)进行数据处理。
- 索引 (Indexing):支持多种类型的索引,可以显著提高查询效率。
- 地理空间索引 (Geospatial Indexing):原生支持地理位置数据的存储和查询。
1.4 MongoDB 与关系型数据库 (RDBMS) 的对比
特性 | MongoDB (NoSQL 文档数据库) | RDBMS (如 MySQL, PostgreSQL) |
---|---|---|
数据模型 | 文档 (Document),类似于 JSON/BSON | 表 (Table),由行 (Row) 和列 (Column) 组成 |
模式 | 无模式/灵活模式 | 固定模式 (Schema) |
关联关系 | 通过内嵌文档或引用 (Reference) 实现 | 通过主键/外键进行 Join 操作实现 |
事务 | 早期版本仅支持单文档事务,新版本支持多文档事务 | 支持 ACID 事务 |
扩展性 | 易于横向扩展 (分片 Sharding) | 垂直扩展为主,横向扩展复杂 (分库分表) |
查询语言 | 基于文档的查询语言,聚合框架 | SQL (Structured Query Language) |
数据存储 | BSON 格式文件 | 结构化文件或页 |
典型应用 | Web 应用、移动应用、内容管理、大数据等 | 金融、电商、ERP 等需要强事务和固定模式的应用 |
理解这些基本概念和对比,有助于你更好地把握 MongoDB 的优势和适用场景。
第二章:安装与配置 MongoDB
学习 MongoDB 的第一步是在你的计算机上安装它。MongoDB 支持多种操作系统,包括 Windows、macOS 和 Linux。这里我们以通用的安装步骤进行说明,具体细节请参考官方文档。
2.1 下载 MongoDB
访问 MongoDB 官方下载页面:https://www.mongodb.com/try/download/community
选择适合你操作系统的版本(Community Server 是免费版本)。
2.2 安装步骤 (以通用流程说明)
- Windows:
- 运行下载的
.msi
安装包。 - 选择 “Custom” 安装类型,以便指定安装路径。
- 勾选 “Install MongoDB Compass” (这是一个图形化管理工具,对初学者很有帮助)。
- 选择 “Run service as Network Service user” 或 “Run service as a local user”,根据需要配置服务。
- 完成安装。
- 运行下载的
- macOS:
- 使用 Homebrew 安装(推荐):打开终端,运行
brew install mongodb-community
。 - 或者下载
.tgz
文件手动安装,解压到指定目录。
- 使用 Homebrew 安装(推荐):打开终端,运行
- Linux:
- 根据你的 Linux 发行版(Ubuntu, CentOS, Debian 等),使用相应的包管理器进行安装(如
apt
或yum
)。例如,在 Ubuntu 上可以按照官方指南添加源并运行sudo apt-get install -y mongodb-org
。 - 或者下载
.tgz
文件手动安装,解压并设置环境变量。
- 根据你的 Linux 发行版(Ubuntu, CentOS, Debian 等),使用相应的包管理器进行安装(如
2.3 启动 MongoDB 服务器 (mongod)
安装完成后,你需要启动 MongoDB 服务器进程,它负责管理数据存储和客户端连接。
- Windows:
- 如果安装时选择作为服务运行,它可能已经自动启动了。你可以通过服务管理器检查或启动。
- 手动启动:打开命令提示符或 PowerShell,切换到 MongoDB 安装目录的
bin
目录,然后运行mongod.exe --dbpath <你的数据存储路径>
。你需要创建一个用于存储数据的目录(例如C:\data\db
),并在--dbpath
参数中指定。
- macOS (使用 Homebrew):
- 运行
brew services start mongodb-community
。 - 手动启动:运行
mongod --dbpath <你的数据存储路径>
。你需要创建一个用于存储数据的目录(例如/usr/local/var/mongodb
),并在--dbpath
参数中指定。
- 运行
- Linux:
- 大多数包管理器安装会将其配置为系统服务,使用
sudo systemctl start mongod
(Systemd 系统) 或sudo service mongod start
(SysVinit 系统) 启动。 - 手动启动:运行
mongod --dbpath <你的数据存储路径>
。你需要创建一个用于存储数据的目录,并在--dbpath
参数中指定。
- 大多数包管理器安装会将其配置为系统服务,使用
成功启动后,你会看到类似 “waiting for connections on port 27017” 的日志信息,这表示 MongoDB 服务器正在运行并监听默认端口 27017。
2.4 连接 MongoDB (使用 mongosh Shell)
MongoDB Shell 是与 MongoDB 交互的主要命令行工具。新版本的 Shell 叫做 mongosh
,老版本是 mongo
。推荐使用 mongosh
。
打开一个新的终端或命令提示符窗口,运行:
bash
mongosh
如果 MongoDB 服务器正在本地运行且监听默认端口,mongosh
会自动连接。连接成功后,你会看到 MongoDB 的版本信息和提示符,例如:
“`
“`
现在你已经成功连接到 MongoDB 服务器,可以开始学习如何操作数据库了!
第三章:MongoDB 的核心概念详解
在开始实际操作之前,我们再次明确 MongoDB 的几个核心概念:数据库、集合和文档。
3.1 数据库 (Database)
- 在 MongoDB 中,数据库是文档的物理容器。
- 每个数据库都有自己的一组文件在文件系统上。
- 一个 MongoDB 服务器可以托管多个数据库。
- 你可以使用
use <database_name>
命令切换到指定的数据库。如果指定的数据库不存在,MongoDB 会在第一次向其中存储数据时自动创建它。 - 默认数据库是
test
。
3.2 集合 (Collection)
- 集合类似于关系型数据库中的“表”。
- 集合是文档的组。
- 集合存在于数据库中。一个数据库可以包含多个集合。
- 与关系型数据库的表不同,集合没有固定的模式 (Schema)。这意味着集合中的文档可以有不同的字段和结构,尽管在实际应用中,通常同一个集合中的文档会有一个大致相似的结构。
3.3 文档 (Document)
- 文档是 MongoDB 的核心单元,类似于关系型数据库中的“行”。
- 文档是键值对的有序集合,使用 BSON(Binary JSON)格式表示。
- 键是字符串,值可以是各种数据类型,包括字符串、数字、布尔值、数组、嵌套文档、日期、ObjectID 等。
- 每个文档都有一个唯一的
_id
字段,它作为文档的主键。如果你插入文档时没有指定_id
,MongoDB 会自动生成一个唯一的 ObjectID。
示例文档 (JSON 格式):
json
{
"_id": ObjectId("60b8d6c2b3d6f7c2a8c1b2a3"),
"name": "Alice",
"age": 30,
"isStudent": false,
"courses": ["Math", "Science", "History"],
"address": {
"street": "123 Main St",
"city": "New York",
"zip": "10001"
},
"enrollmentDate": ISODate("2023-09-01T10:00:00Z")
}
理解这三个层级关系(服务器 -> 数据库 -> 集合 -> 文档)是学习 MongoDB 操作的基础。
第四章:基本 CRUD 操作 (Create, Read, Update, Delete)
现在,让我们通过 MongoDB Shell (mongosh
) 来执行最基本的数据库操作:创建(Insert)、读取(Find)、更新(Update)和删除(Delete)。
我们将使用一个名为 mydatabase
的数据库,并在其中创建一个名为 students
的集合。
4.1 切换/创建数据库
首先,连接到 mongosh
,然后切换到 mydatabase
数据库。如果该数据库不存在,MongoDB 会在你第一次插入数据时创建它。
javascript
use mydatabase;
输出:
changed to db mydatabase
现在,你已经切换到了 mydatabase
。当前数据库的名称会显示在提示符前(如果配置了的话),或者你可以使用 db
命令查看:
javascript
db;
输出:
mydatabase
4.2 创建集合 (可选,首次插入时自动创建)
你可以使用 db.createCollection()
显式创建一个集合,但这通常不是必需的,因为在你第一次向一个不存在的集合插入文档时,MongoDB 会自动创建该集合。
javascript
db.createCollection("students");
输出:
{ ok: 1 }
你可以使用 show collections
命令查看当前数据库中的集合:
javascript
show collections;
输出:
students
4.3 创建 (Insert) 文档
向集合中添加新文档。
-
插入单个文档: 使用
insertOne()
方法。javascript
db.students.insertOne({
name: "Alice",
age: 20,
major: "Computer Science",
gpa: 3.8,
courses: ["Calculus", "Physics", "Programming"]
});成功插入后,会返回一个包含
insertedId
的结果对象。 -
插入多个文档: 使用
insertMany()
方法,参数是一个文档数组。javascript
db.students.insertMany([
{
name: "Bob",
age: 22,
major: "Mathematics",
gpa: 3.5,
courses: ["Calculus", "Linear Algebra"]
},
{
name: "Charlie",
age: 19,
major: "Physics",
gpa: 3.9,
courses: ["Physics", "Astronomy", "Calculus"],
advisor: {
name: "Dr. Smith",
department: "Physics"
}
},
{
name: "David",
age: 21,
major: "Computer Science",
gpa: 3.6,
courses: ["Programming", "Data Structures"]
}
]);成功插入多个文档会返回包含
insertedIds
数组的结果对象。
此时,students
集合中已经有了 4 个文档。注意 “Charlie” 的文档结构与其他文档略有不同,它包含了一个内嵌文档 advisor
,这体现了 MongoDB 的灵活模式特性。
4.4 读取 (Find) 文档
从集合中查询文档。
-
查询所有文档: 使用
find()
方法,不带任何参数。javascript
db.students.find();这会返回一个游标 (Cursor),
mongosh
会默认打印游标的前20个结果。 -
查询符合条件的文档: 在
find()
方法的第一个参数中传入一个查询文档(Filter Document)。查询文档指定了要匹配的条件。“`javascript
// 查询 age 等于 20 的学生
db.students.find({ age: 20 });// 查询 major 是 “Computer Science” 的学生
db.students.find({ major: “Computer Science” });// 查询 GPA 大于等于 3.8 的学生 (使用比较运算符)
db.students.find({ gpa: { $gte: 3.8 } });
“`常用的比较运算符:
*$eq
: 等于 (可以省略,直接写{ field: value }
)
*$ne
: 不等于
*$gt
: 大于
*$lt
: 小于
*$gte
: 大于等于
*$lte
: 小于等于
*$in
: 存在于指定数组中
*$nin
: 不存在于指定数组中更多查询示例:
“`javascript
// 查询 age 小于 22 且 major 是 “Physics” 的学生 ($and 操作符或隐式 AND)
db.students.find({ age: { $lt: 22 }, major: “Physics” });// 查询 major 是 “Mathematics” 或 “Physics” 的学生 ($or 操作符)
db.students.find({ $or: [{ major: “Mathematics” }, { major: “Physics” }] });// 查询 courses 数组中包含 “Calculus” 的学生
db.students.find({ courses: “Calculus” }); // 注意: 如果数组字段包含指定值,直接写即可// 查询 courses 数组精确匹配 [“Programming”, “Data Structures”] 的学生 (顺序和内容都需匹配)
db.students.find({ courses: [“Programming”, “Data Structures”] });// 查询存在 advisor 字段的学生 ($exists 操作符)
db.students.find({ advisor: { $exists: true } });// 查询 age 字段类型是 number 的学生 ($type 操作符)
db.students.find({ age: { $type: “number” } });
// 可以使用 BSON 类型别名或编号:number 是 16 或 18// 查询内嵌文档字段
db.students.find({ “advisor.department”: “Physics” }); // 使用点表示法访问内嵌字段
“` -
查询单个文档: 使用
findOne()
方法,它返回第一个匹配的文档或者 null。javascript
db.students.findOne({ name: "Alice" }); -
投影 (Projection): 在
find()
方法的第二个参数中传入一个投影文档,指定返回的字段。1
表示包含该字段,0
表示排除该字段。_id
字段默认包含,除非显式排除。“`javascript
// 只返回 name 和 major 字段 (_id 默认也会返回)
db.students.find({}, { name: 1, major: 1 });// 返回所有字段,但不包括 _id
db.students.find({}, { _id: 0, name: 1, major: 1, age: 1, gpa: 1, courses: 1, advisor: 1 });// 只返回 name 和 age 字段,并排除 _id
db.students.find({}, { _id: 0, name: 1, age: 1 });
“` -
排序 (Sort): 使用
sort()
方法对结果进行排序。参数是一个排序文档,1
表示升序,-1
表示降序。“`javascript
// 按 age 升序排序
db.students.find().sort({ age: 1 });// 按 gpa 降序排序
db.students.find().sort({ gpa: -1 });// 先按 major 升序,再按 age 降序排序
db.students.find().sort({ major: 1, age: -1 });
“` -
限制返回数量 (Limit): 使用
limit()
方法限制返回的文档数量。javascript
// 只返回前 2 个文档
db.students.find().limit(2); -
跳过指定数量 (Skip): 使用
skip()
方法跳过指定数量的文档。常用于分页。“`javascript
// 跳过前 1 个文档,返回后面的文档
db.students.find().skip(1);// 分页示例:每页 2 个,获取第 2 页 (跳过 2 * (2-1) = 2 个)
db.students.find().skip(2).limit(2);
“` -
链式调用:
find()
返回的游标对象支持链式调用sort()
,limit()
,skip()
等方法。javascript
// 查询 major 是 Computer Science 的学生,按 age 降序排序,只返回前 1 个
db.students.find({ major: "Computer Science" }).sort({ age: -1 }).limit(1);
4.5 更新 (Update) 文档
修改集合中已有的文档。
-
更新单个文档: 使用
updateOne()
方法。第一个参数是查询文档(用于匹配要更新的文档),第二个参数是更新文档(指定如何修改)。javascript
// 找到 name 为 "Alice" 的文档,将其 GPA 修改为 3.9
db.students.updateOne(
{ name: "Alice" },
{ $set: { gpa: 3.9 } }
);这里使用了
$set
更新操作符,它用于设置或修改指定字段的值。如果字段不存在,则会添加该字段。其他常用的更新操作符:
*$inc
: 增加/减少数字字段的值。
*$unset
: 移除指定的字段。
*$push
: 向数组字段的末尾添加一个元素。
*$addToSet
: 向数组字段添加一个元素,但只在该元素不存在时添加(避免重复)。
*$pop
: 移除数组的第一个或最后一个元素。
*$pull
: 移除数组中所有匹配指定条件的元素。更多更新示例:
“`javascript
// 找到 name 为 “Bob” 的文档,将 age 增加 1 ($inc)
db.students.updateOne(
{ name: “Bob” },
{ $inc: { age: 1 } }
);// 找到 name 为 “Charlie” 的文档,移除 advisor 字段 ($unset)
db.students.updateOne(
{ name: “Charlie” },
{ $unset: { advisor: “” } } // $unset 的值可以是任意类型
);// 找到 name 为 “David” 的文档,向 courses 数组添加 “Database Systems” ($push)
db.students.updateOne(
{ name: “David” },
{ $push: { courses: “Database Systems” } }
);// 找到 name 为 “Alice” 的文档,如果 “Networking” 不在 courses 中,则添加 ($addToSet)
db.students.updateOne(
{ name: “Alice” },
{ $addToSet: { courses: “Networking” } }
);
“` -
更新多个文档: 使用
updateMany()
方法,更新所有匹配查询条件的文档。“`javascript
// 将所有 major 是 “Computer Science” 的学生的 GPA 都增加 0.1
db.students.updateMany(
{ major: “Computer Science” },
{ $inc: { gpa: 0.1 } }
);// 给所有文档添加一个 status 字段,默认值为 “active”
db.students.updateMany(
{}, // 空查询文档匹配所有文档
{ $set: { status: “active” } }
);
“` -
替换文档: 使用
replaceOne()
方法,用一个全新的文档替换第一个匹配的文档。注意,新文档必须包含_id
字段(通常使用原文档的_id
),并且不能包含更新操作符。javascript
// 找到 name 为 "Bob" 的文档,用一个全新的文档替换它
const bobDocument = db.students.findOne({ name: "Bob" });
db.students.replaceOne(
{ _id: bobDocument._id }, // 使用原文档的 _id 进行匹配
{
name: "Bob Updated",
age: 23,
major: "Applied Mathematics" // 注意:courses, gpa 等字段被移除了
}
);
4.6 删除 (Delete) 文档
从集合中移除文档。
-
删除单个文档: 使用
deleteOne()
方法,删除第一个匹配查询条件的文档。javascript
// 删除 age 小于 20 的第一个文档
db.students.deleteOne({ age: { $lt: 20 } }); -
删除多个文档: 使用
deleteMany()
方法,删除所有匹配查询条件的文档。“`javascript
// 删除所有 major 是 “Mathematics” 的文档
db.students.deleteMany({ major: “Mathematics” });// 删除 students 集合中的所有文档 (传入空查询文档 {})
// db.students.deleteMany({});
“` -
删除集合: 使用
drop()
方法删除整个集合,包括所有文档和索引。javascript
db.students.drop(); -
删除数据库: 切换到要删除的数据库,然后使用
dropDatabase()
方法。javascript
use mydatabase; // 切换到 mydatabase
db.dropDatabase(); // 删除当前数据库删除数据库后,通常会自动切换回
test
数据库。
至此,你已经掌握了 MongoDB 中最核心的 CRUD 操作。通过这些命令,你可以在 Shell 中对文档进行基本的增、删、改、查。
第五章:索引 (Indexing) 入门
索引是提高数据库查询性能的关键。在 MongoDB 中,索引的工作原理与关系型数据库类似,它们存储了少量、易于遍历的数据结构,指向集合中完整文档的位置。
5.1 为什么需要索引?
如果没有索引,MongoDB 需要执行全集合扫描 (Collection Scan) 来查找匹配的文档,这对于大型集合来说效率非常低下。索引可以帮助 MongoDB 快速定位文档,大幅减少需要扫描的数据量。
5.2 创建索引
使用 createIndex()
方法创建索引。第一个参数是索引键文档,指定要创建索引的字段和排序方式(1
为升序,-1
为降序)。
“`javascript
// 在 name 字段上创建升序索引
db.students.createIndex({ name: 1 });
// 在 major 字段上创建降序索引
db.students.createIndex({ major: -1 });
// 创建复合索引:先按 major 升序,再按 age 降序
db.students.createIndex({ major: 1, age: -1 });
“`
通常在哪些字段上创建索引?
* 经常用于查询条件 (find()
) 的字段。
* 用于排序 (sort()
) 的字段。
* 用于连接操作(如果使用 $lookup
聚合阶段模拟 JOIN)的字段。
* _id
字段上默认有一个唯一的升序索引。
5.3 查看索引
使用 getIndexes()
方法查看集合上的所有索引。
javascript
db.students.getIndexes();
输出会是一个包含索引信息的数组,包括默认的 _id
索引和你创建的索引。
5.4 删除索引
使用 dropIndex()
方法删除指定的索引。参数可以是索引名称(默认名称格式是 字段名_排序
,如 name_1
)或索引键文档。
“`javascript
// 删除 name 字段上的索引
db.students.dropIndex(“name_1”);
// 删除 major 字段上的降序索引 (使用索引键文档)
// db.students.dropIndex({ major: -1 });
// 删除所有索引 (除了 _id 索引)
// db.students.dropIndexes();
“`
索引注意事项:
* 索引会占用存储空间。
* 索引会影响写入(Insert, Update, Delete)性能,因为每次写入操作都需要更新索引。
* 不是所有字段都适合创建索引,例如包含大量重复值的字段(低选择度字段)索引效果可能不明显。
* 复合索引的字段顺序很重要。
第六章:数据建模基础 (嵌入 vs 引用)
关系型数据库中的数据建模通常遵循范式理论,通过规范化减少数据冗余,并通过 Join 操作查询关联数据。MongoDB 作为文档数据库,提供了更灵活的数据建模方式,主要包括 嵌入 (Embedded) 和 引用 (Referenced) 两种模式。
选择哪种模式取决于数据之间的关系、访问模式以及性能需求。
6.1 嵌入 (Embedded)
如果两个实体之间存在一对一或一对多的关系,并且“多”的那一方在逻辑上是“一”的那一方的一部分,或者它们经常一起被访问,那么可以考虑将“多”嵌入到“一”的文档中。
示例: 用户和地址。一个用户可能有多个地址(家庭地址、工作地址等),地址信息经常与用户信息一起被访问。
RDBMS 模式:
“`sql
— user 表
CREATE TABLE users (
user_id INT PRIMARY KEY,
name VARCHAR(255),
…
);
— address 表
CREATE TABLE addresses (
address_id INT PRIMARY KEY,
user_id INT, — 外键引用 users 表
street VARCHAR(255),
city VARCHAR(255),
…
FOREIGN KEY (user_id) REFERENCES users(user_id)
);
“`
查询用户及其地址需要 JOIN 操作。
MongoDB 嵌入模式:
json
{
"_id": ObjectId(...),
"name": "Alice",
"age": 30,
"addresses": [
{
"street": "123 Main St",
"city": "New York",
"zip": "10001",
"type": "home"
},
{
"street": "456 Oak Ave",
"city": "Los Angeles",
"zip": "90001",
"type": "work"
}
]
}
查询用户及其所有地址只需要一次读取操作。
优点:
* 读取性能高:相关数据在同一个文档中,减少了查询次数。
* 原子性:对文档的单个写入操作是原子性的。
缺点:
* 文档大小限制:MongoDB 文档最大不能超过 16MB。
* 嵌入数组的大小增长问题:如果嵌入的数组会无限增长,可能导致文档过大或频繁更新开销。
* 更新复杂性:更新内嵌数组中的某个元素可能需要特定操作符。
6.2 引用 (Referenced)
如果实体之间是复杂的多对多关系,或者“多”的那一方数量非常大,或者实体需要独立存在并在多个地方被引用,那么应该使用引用模式。通过在文档中存储被引用文档的 _id
来建立连接。
示例: 学生和课程。一个学生可以选择多门课程,一门课程可以有多个学生。
RDBMS 模式:
“`sql
— students 表
CREATE TABLE students ( … );
— courses 表
CREATE TABLE courses ( … );
— student_courses 关联表 (多对多)
CREATE TABLE student_courses (
student_id INT, — 外键
course_id INT, — 外键
PRIMARY KEY (student_id, course_id)
);
“`
MongoDB 引用模式:
“`json
// students 集合
{
“_id”: ObjectId(“studentA”),
“name”: “Alice”,
“enrolled_course_ids”: [ // 引用 courses 集合的 _id
ObjectId(“course101”),
ObjectId(“course205”)
]
}
// courses 集合
{
“_id”: ObjectId(“course101”),
“title”: “Calculus I”,
// 可选: 存储引用该课程的学生 ID 数组 (方便通过课程查找学生)
“student_ids”: [
ObjectId(“studentA”),
ObjectId(“studentB”)
]
}
“`
查询学生的课程或课程的学生需要进行两次查询(先查一边的文档,再用查到的 _id
列表去查另一边的文档)。或者使用聚合框架的 $lookup
阶段模拟 JOIN。
优点:
* 避免文档大小限制。
* 适合表示复杂的多对多关系。
* 实体可以独立管理和更新。
缺点:
* 读取通常需要多次查询或使用 $lookup
,相比嵌入模式性能可能略低。
数据建模总结:
选择嵌入还是引用模式是一个权衡过程。通常优先考虑嵌入,因为它能带来更好的读取性能和原子性,尤其是在数据访问模式是“整体获取”时。当存在以下情况时,考虑引用:
* 存在复杂的多对多关系。
* 被关联的数据量非常大或无限增长。
* 被关联的实体需要独立存在或经常被独立访问/更新。
这只是数据建模的入门概念,实际应用中还有更复杂的场景和模式。
第七章:MongoDB Compass (图形化工具)
虽然 mongosh
是强大的命令行工具,但对于初学者来说,使用图形化界面工具可以更直观地进行学习和操作。MongoDB Compass 是官方提供的免费图形化工具,功能强大且易于使用。
7.1 安装 Compass
如果在安装 MongoDB 时勾选了安装 Compass,它应该已经安装了。如果没有,可以从 MongoDB 官方网站单独下载和安装 Compass:https://www.mongodb.com/try/download/compass
7.2 连接到 MongoDB
打开 Compass,通常会自动检测本地运行的 MongoDB 实例。如果需要手动连接,点击 “New Connection”,填写连接信息(默认是 mongodb://localhost:27017/
),然后点击 “Connect”。
7.3 使用 Compass
连接成功后,Compass 会显示服务器上的数据库列表。
* 点击一个数据库可以查看其中的集合列表。
* 点击一个集合可以查看其中的文档。Compass 以列表、表格或 JSON 格式展示文档,并提供了直观的过滤、排序、投影和分页选项。
* 你可以使用界面上的按钮轻松进行插入、编辑(更新)和删除文档的操作。
* Compass 还提供了 Schema 分析、索引管理、Aggregation Pipeline 构建等高级功能的可视化界面,对理解和使用这些功能非常有帮助。
强烈建议在学习过程中同时使用 mongosh
和 Compass。mongosh
帮助你理解命令和底层机制,而 Compass 提供了直观的视图和便捷的操作方式。
第八章:总结与下一步
恭喜你!通过本文的学习,你已经掌握了 MongoDB 的基本概念、安装方法、以及最核心的 CRUD 操作、索引基础和数据建模初步知识。这为你进一步深入学习 MongoDB 打下了坚实的基础。
回顾我们学到的:
- MongoDB 是一个灵活的文档数据库,适合快速开发和处理海量数据。
- MongoDB 的核心是数据库、集合和文档。
- 我们学会了使用
mongosh
连接数据库并执行use
、show collections
等命令。 - 掌握了
insertOne()
,insertMany()
,find()
,findOne()
,updateOne()
,updateMany()
,deleteOne()
,deleteMany()
这些基本的 CRUD 操作。 - 学习了
find()
方法中的查询条件、投影、排序、限制和跳过。 - 了解了如何使用
$set
,$inc
,$push
等更新操作符。 - 理解了索引的作用,学会了创建和查看索引。
- 初步了解了 MongoDB 的两种主要数据建模方式:嵌入和引用。
- 认识了 MongoDB Compass 这个图形化工具。
下一步的学习方向:
- 深入查询: 学习更多高级查询技巧,如正则表达式
$regex
、地理空间查询$geoWithin
、数组查询$elemMatch
等。 - 聚合框架 (Aggregation Framework): 这是 MongoDB 中强大的数据处理工具,用于进行分组、计算、转换等复杂操作。学习
$match
,$group
,$project
,$sort
,$limit
,$unwind
,$lookup
等常用阶段。 - 复制集 (Replica Set): 学习如何搭建和管理复制集,实现数据冗余和高可用性。
- 分片 (Sharding): 学习如何搭建和管理分片集群,实现数据的横向扩展,处理超大规模数据。
- 事务 (Transactions): 了解 MongoDB 新版本对多文档事务的支持和使用方法。
- 安全: 学习用户认证、权限控制等安全配置。
- 性能优化: 学习如何分析慢查询、优化索引、调整配置等。
- 驱动程序 (Drivers): 学习如何在各种编程语言(如 Node.js, Python, Java, Go 等)中使用 MongoDB 官方或社区驱动程序连接和操作 MongoDB 数据库。
- MongoDB Atlas: 体验 MongoDB 官方提供的云数据库服务,了解如何在云端部署和管理 MongoDB。
学习任何新技术都需要持续的实践。尝试用 MongoDB 实现一些小项目,比如博客系统、用户管理系统等,将你学到的知识应用到实际场景中。遇到问题时,查阅 MongoDB 官方文档是最好的资源。
祝你在 MongoDB 的学习旅程中一切顺利!