MongoDB 入门教程:从零开始学 – wiki基地


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 文件手动安装,解压到指定目录。
  • Linux:
    • 根据你的 Linux 发行版(Ubuntu, CentOS, Debian 等),使用相应的包管理器进行安装(如 aptyum)。例如,在 Ubuntu 上可以按照官方指南添加源并运行 sudo apt-get install -y mongodb-org
    • 或者下载 .tgz 文件手动安装,解压并设置环境变量。

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 连接数据库并执行 useshow collections 等命令。
  • 掌握了 insertOne(), insertMany(), find(), findOne(), updateOne(), updateMany(), deleteOne(), deleteMany() 这些基本的 CRUD 操作。
  • 学习了 find() 方法中的查询条件、投影、排序、限制和跳过。
  • 了解了如何使用 $set, $inc, $push 等更新操作符。
  • 理解了索引的作用,学会了创建和查看索引。
  • 初步了解了 MongoDB 的两种主要数据建模方式:嵌入和引用。
  • 认识了 MongoDB Compass 这个图形化工具。

下一步的学习方向:

  1. 深入查询: 学习更多高级查询技巧,如正则表达式 $regex、地理空间查询 $geoWithin、数组查询 $elemMatch 等。
  2. 聚合框架 (Aggregation Framework): 这是 MongoDB 中强大的数据处理工具,用于进行分组、计算、转换等复杂操作。学习 $match, $group, $project, $sort, $limit, $unwind, $lookup 等常用阶段。
  3. 复制集 (Replica Set): 学习如何搭建和管理复制集,实现数据冗余和高可用性。
  4. 分片 (Sharding): 学习如何搭建和管理分片集群,实现数据的横向扩展,处理超大规模数据。
  5. 事务 (Transactions): 了解 MongoDB 新版本对多文档事务的支持和使用方法。
  6. 安全: 学习用户认证、权限控制等安全配置。
  7. 性能优化: 学习如何分析慢查询、优化索引、调整配置等。
  8. 驱动程序 (Drivers): 学习如何在各种编程语言(如 Node.js, Python, Java, Go 等)中使用 MongoDB 官方或社区驱动程序连接和操作 MongoDB 数据库。
  9. MongoDB Atlas: 体验 MongoDB 官方提供的云数据库服务,了解如何在云端部署和管理 MongoDB。

学习任何新技术都需要持续的实践。尝试用 MongoDB 实现一些小项目,比如博客系统、用户管理系统等,将你学到的知识应用到实际场景中。遇到问题时,查阅 MongoDB 官方文档是最好的资源。

祝你在 MongoDB 的学习旅程中一切顺利!


发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部