外观
MongoDB 系统学习文档
适合人群:已经了解基础后端开发,想系统掌握 NoSQL 文档数据库、MongoDB 数据建模、CRUD、索引、聚合管道和生产实践的学习者。
学习目标:从集合和文档入门,逐步掌握 MongoDB 的查询语法、嵌入式建模、引用关系、索引、聚合管道、事务、副本集、分片和与关系型数据库的差异。
核对时间:2026-06-25。本文以 MongoDB 官方文档当前信息为基础整理,重点参考 CRUD、Indexes、Aggregation Pipeline、Transactions 和 Data Modeling 文档。
官方资料:
- MongoDB Docs:https://www.mongodb.com/docs/
- CRUD Operations:https://www.mongodb.com/docs/manual/crud/
- Data Modeling:https://www.mongodb.com/docs/manual/data-modeling/
- Indexes:https://www.mongodb.com/docs/manual/indexes/
- Aggregation Pipeline:https://www.mongodb.com/docs/manual/core/aggregation-pipeline/
- Transactions:https://www.mongodb.com/docs/manual/core/transactions/
目录
第一篇:基础认知
- MongoDB 是什么
- SQL 和 NoSQL 的区别
- Database、Collection、Document
- BSON 和 JSON
- 安装与连接
第二篇:CRUD 基础
- 插入文档
- 查询文档
- 更新文档
- 删除文档
- 条件、排序和分页
- 投影 projection
第三篇:数据建模
- 文档模型思维
- 嵌入式文档
- 引用关系
- 一对一、一对多、多对多
- Schema 设计与校验
- 关系型建模和文档建模的区别
第四篇:索引与聚合
- 索引基础
- 复合索引
- 唯一索引、TTL 索引、文本索引
- explain 分析
- 聚合管道
- 常见聚合场景
第五篇:事务、部署与生产实践
- MongoDB 事务
- 副本集
- 分片
- Node.js/Mongoose/Prisma 接入
- 性能和安全
- 常见误区
- 学习路线和练习项目
第一篇:基础认知
1. MongoDB 是什么
MongoDB 是一个文档型 NoSQL 数据库。它不使用表和行作为核心模型,而是使用集合和文档。
关系型数据库中的记录可能长这样:
text
users 表
- id
- name
- emailMongoDB 文档可能长这样:
json
{
"_id": "665f...",
"name": "Alice",
"email": "alice@example.com",
"profile": {
"age": 28,
"city": "Shanghai"
},
"tags": ["vip", "newsletter"]
}MongoDB 适合:
- 文档结构灵活的业务。
- 内容管理、配置、日志、事件、商品属性等半结构化数据。
- 读写一个完整业务对象比多表 join 更常见的场景。
- 快速迭代、字段变化频繁的项目。
但 MongoDB 不是“万能数据库”。如果业务强依赖复杂事务、多表关联、严格关系约束,关系型数据库可能更合适。
2. SQL 和 NoSQL 的区别
SQL 数据库:
- 表结构明确。
- 使用 SQL 查询。
- 强调关系、约束、事务。
- 常见产品:PostgreSQL、MySQL。
NoSQL 数据库:
- 数据模型更灵活。
- 查询语言因产品而异。
- 常见类型包括文档、键值、列族、图数据库。
- MongoDB 属于文档数据库。
区别不是“SQL 老,NoSQL 新”。二者解决的问题不同。
关系型思维:
text
用户表 + 订单表 + 订单项表 + 商品表文档型思维:
text
订单文档中嵌入订单项快照MongoDB 建模时要先问:应用最常读取的数据形状是什么?
3. Database、Collection、Document
MongoDB 层级:
text
MongoDB Deployment
└── database
└── collection
└── document对比关系型数据库:
| MongoDB | PostgreSQL/MySQL |
|---|---|
| database | database |
| collection | table |
| document | row |
| field | column |
| _id | primary key |
注意:这是帮助理解的类比,不代表二者完全等价。MongoDB 文档可以嵌套对象和数组,而关系型表更强调行列结构。
4. BSON 和 JSON
MongoDB 文档看起来像 JSON,但底层使用 BSON。BSON 是 Binary JSON,支持更多类型,例如:
- ObjectId
- Date
- Decimal128
- Binary
- Regular Expression
示例:
js
{
_id: ObjectId("665f0a..."),
createdAt: ISODate("2026-06-25T00:00:00Z"),
price: Decimal128("99.90")
}学习时可以先把文档理解为 JSON,但生产中要注意 ObjectId、Date、Decimal 等类型的行为。
5. 安装与连接
Docker 示例:
bash
docker run --name mongo-demo \
-p 27017:27017 \
-d mongo:8连接字符串:
text
mongodb://localhost:27017/appdb使用 MongoDB Shell:
bash
mongosh "mongodb://localhost:27017/appdb"常用命令:
js
show dbs
use appdb
show collections
db.users.find()第二篇:CRUD 基础
6. 插入文档
插入单条:
js
db.users.insertOne({
name: "Alice",
email: "alice@example.com",
createdAt: new Date()
});插入多条:
js
db.users.insertMany([
{ name: "Alice", email: "alice@example.com" },
{ name: "Bob", email: "bob@example.com" }
]);如果没有提供 _id,MongoDB 会自动生成 ObjectId。
7. 查询文档
查询所有:
js
db.users.find();按条件查询:
js
db.users.find({
email: "alice@example.com"
});比较查询:
js
db.orders.find({
amount: { $gte: 100 }
});数组查询:
js
db.users.find({
tags: "vip"
});嵌套字段:
js
db.users.find({
"profile.city": "Shanghai"
});8. 更新文档
更新一条:
js
db.users.updateOne(
{ email: "alice@example.com" },
{ $set: { name: "Alice Chen" } }
);更新多条:
js
db.users.updateMany(
{ active: false },
{ $set: { archived: true } }
);自增:
js
db.products.updateOne(
{ _id: productId },
{ $inc: { stock: -1 } }
);数组追加:
js
db.users.updateOne(
{ email: "alice@example.com" },
{ $addToSet: { tags: "vip" } }
);常用更新操作符:
$set$unset$inc$push$addToSet$pull
9. 删除文档
删除一条:
js
db.users.deleteOne({
email: "alice@example.com"
});删除多条:
js
db.logs.deleteMany({
createdAt: {
$lt: new Date("2025-01-01")
}
});生产中删除要非常谨慎。对核心业务数据,通常优先考虑软删除:
js
db.users.updateOne(
{ _id: userId },
{ $set: { deletedAt: new Date() } }
);10. 条件、排序和分页
逻辑条件:
js
db.users.find({
$or: [
{ role: "admin" },
{ tags: "vip" }
]
});排序:
js
db.posts.find().sort({ createdAt: -1 });分页:
js
db.posts.find()
.sort({ createdAt: -1 })
.skip(40)
.limit(20);大分页建议使用游标方式:
js
db.posts.find({
_id: { $lt: lastSeenId }
})
.sort({ _id: -1 })
.limit(20);skip 很大时也会有性能问题。
11. 投影 projection
只返回指定字段:
js
db.users.find(
{ active: true },
{ name: 1, email: 1 }
);排除字段:
js
db.users.find(
{},
{ passwordHash: 0 }
);接口返回数据时,不要把敏感字段返回给前端。
第三篇:数据建模
12. 文档模型思维
关系型数据库建模通常先分析实体和关系,再拆表。
MongoDB 建模更关注:
- 应用如何读取数据?
- 哪些数据总是一起展示?
- 哪些数据更新频率不同?
- 文档会不会无限增长?
- 是否需要跨文档事务?
MongoDB 的核心建模选择:
- 嵌入 embedded documents
- 引用 references
没有绝对正确答案,要围绕读写模式设计。
13. 嵌入式文档
订单文档示例:
json
{
"_id": "order1",
"userId": "user1",
"status": "paid",
"items": [
{
"productId": "p1",
"productName": "Keyboard",
"price": 199.00,
"quantity": 1
}
],
"shippingAddress": {
"province": "Shanghai",
"city": "Shanghai",
"detail": "xxx road"
},
"createdAt": "2026-06-25T00:00:00Z"
}适合嵌入:
- 子数据总是和父数据一起读取。
- 子数据生命周期依赖父数据。
- 子数据数量有限。
- 需要保存历史快照。
例如订单项适合嵌入订单,因为订单项通常和订单一起展示,并且需要保存下单时商品快照。
14. 引用关系
引用用户:
json
{
"_id": "post1",
"authorId": "user1",
"title": "MongoDB 入门"
}适合引用:
- 关联数据很大。
- 关联数据经常独立更新。
- 多个文档共享同一份数据。
- 嵌入会导致数据重复过多。
- 子数据数量可能无限增长。
例如文章引用作者,而不是把完整用户资料嵌入每篇文章。否则用户改头像后,大量文章文档都要更新。
15. 一对一、一对多、多对多
一对一嵌入:
json
{
"_id": "user1",
"name": "Alice",
"profile": {
"bio": "Frontend developer",
"avatarUrl": "https://example.com/a.png"
}
}一对多嵌入:
json
{
"_id": "post1",
"title": "文章",
"comments": [
{ "userId": "user1", "content": "不错" },
{ "userId": "user2", "content": "学习了" }
]
}如果评论可能很多,不建议无限嵌入文章文档。可以拆成 comments 集合:
json
{
"_id": "comment1",
"postId": "post1",
"userId": "user1",
"content": "不错"
}多对多可以使用数组引用:
json
{
"_id": "post1",
"tagIds": ["tag1", "tag2"]
}或使用中间集合:
json
{
"postId": "post1",
"tagId": "tag1",
"createdAt": "2026-06-25T00:00:00Z"
}如果关系本身有属性,使用中间集合更清晰。
16. Schema 设计与校验
MongoDB 是灵活 schema,不代表没有 schema。你可以在应用层用 TypeScript、Zod、Mongoose schema,也可以在数据库层使用 JSON Schema validation。
示例:
js
db.createCollection("users", {
validator: {
$jsonSchema: {
bsonType: "object",
required: ["email", "name"],
properties: {
email: { bsonType: "string" },
name: { bsonType: "string" },
age: { bsonType: "int" }
}
}
}
});建议:
- 核心业务字段要有校验。
- 应用层和数据库层都要有基本约束。
- 字段演进要保留兼容性。
- 不要让同一集合里出现大量不兼容文档形状。
17. 关系型建模和文档建模的区别
关系型数据库:
text
users
orders
order_items
productsMongoDB:
text
orders 文档里嵌入 order_items 快照
products 单独集合维护当前商品关系型数据库擅长:
- 强关系
- 复杂 join
- 严格约束
- 多表事务
MongoDB 擅长:
- 一个文档表达一个聚合对象
- 字段灵活
- 嵌套结构
- 快速读取完整对象
建模口诀:
text
一起读取、一起变化、数量有限:考虑嵌入。
独立更新、被多处共享、数量很大:考虑引用。第四篇:索引与聚合
18. 索引基础
创建索引:
js
db.users.createIndex({ email: 1 });查询:
js
db.users.find({ email: "alice@example.com" });索引提升查询速度,但增加写入成本和存储空间。
查看索引:
js
db.users.getIndexes();删除索引:
js
db.users.dropIndex("email_1");19. 复合索引
创建:
js
db.orders.createIndex({
userId: 1,
status: 1,
createdAt: -1
});适合查询:
js
db.orders.find({
userId: "user1",
status: "paid"
}).sort({ createdAt: -1 });复合索引字段顺序很重要。通常把等值过滤字段放前面,排序或范围字段放后面,但要结合真实查询模式。
20. 唯一索引、TTL 索引、文本索引
唯一索引:
js
db.users.createIndex(
{ email: 1 },
{ unique: true }
);TTL 索引用于自动删除过期文档:
js
db.sessions.createIndex(
{ expiresAt: 1 },
{ expireAfterSeconds: 0 }
);文本索引:
js
db.posts.createIndex({
title: "text",
content: "text"
});文本搜索:
js
db.posts.find({
$text: { $search: "mongodb index" }
});复杂全文搜索场景可以考虑专门搜索引擎或 MongoDB Atlas Search。
21. explain 分析
js
db.orders.find({
userId: "user1",
status: "paid"
}).explain("executionStats");关注:
- 是否使用索引。
- 扫描了多少文档。
- 返回了多少文档。
- 是否出现 collection scan。
优化原则:
- 高频过滤字段建立索引。
- 排序字段和过滤字段协调设计复合索引。
- 避免返回不必要字段。
- 大集合查询必须关注 explain。
22. 聚合管道
聚合管道由多个 stage 组成,文档依次经过每个 stage。
示例:统计每个状态的订单金额。
js
db.orders.aggregate([
{ $match: { createdAt: { $gte: new Date("2026-01-01") } } },
{
$group: {
_id: "$status",
totalAmount: { $sum: "$amount" },
count: { $sum: 1 }
}
},
{ $sort: { totalAmount: -1 } }
]);常见 stage:
$match:过滤$project:选择和计算字段$group:分组$sort:排序$limit:限制数量$skip:跳过$lookup:类似 join$unwind:展开数组
一般把 $match 尽量放前面,减少后续处理的数据量。
23. 常见聚合场景
按天统计:
js
db.orders.aggregate([
{
$group: {
_id: {
$dateToString: {
format: "%Y-%m-%d",
date: "$createdAt"
}
},
count: { $sum: 1 },
amount: { $sum: "$amount" }
}
},
{ $sort: { _id: 1 } }
]);展开订单项:
js
db.orders.aggregate([
{ $unwind: "$items" },
{
$group: {
_id: "$items.productId",
quantity: { $sum: "$items.quantity" }
}
}
]);关联查询:
js
db.posts.aggregate([
{
$lookup: {
from: "users",
localField: "authorId",
foreignField: "_id",
as: "author"
}
}
]);$lookup 能做关联,但不要把 MongoDB 当成关系型数据库大量 join。频繁复杂 join 可能说明数据模型不适合 MongoDB。
第五篇:事务、部署与生产实践
24. MongoDB 事务
MongoDB 支持多文档事务,但使用时要谨慎。文档数据库设计应尽量让一个业务聚合能在单文档内原子更新。
单文档更新天然是原子的:
js
db.products.updateOne(
{ _id: productId, stock: { $gt: 0 } },
{ $inc: { stock: -1 } }
);多文档事务适合:
- 必须同时更新多个集合。
- 财务、库存、订单等强一致操作。
- 不能通过单文档模型解决的一致性需求。
事务会增加复杂度和性能成本。不要因为 MongoDB 支持事务,就把它建模成关系型数据库。
25. 副本集
副本集用于高可用。
text
Primary
├── Secondary
└── SecondaryPrimary 处理写入,Secondary 复制数据。Primary 故障时,副本集可以选举新的 Primary。
副本集价值:
- 高可用
- 数据冗余
- 读扩展
- 支持事务等能力
生产环境不应该只运行单节点 MongoDB。
26. 分片
分片用于水平扩展,把数据分布到多个 shard。
适合:
- 数据量非常大。
- 单机存储或写入扛不住。
- 查询可以通过合理 shard key 分散。
分片关键是 shard key 选择。糟糕的 shard key 会导致热点、数据倾斜和查询低效。
普通项目不要过早分片。先做好索引、数据模型、硬件和副本集。
27. Node.js/Mongoose/Prisma 接入
MongoDB Node.js Driver:
ts
import { MongoClient } from "mongodb";
const client = new MongoClient(process.env.MONGODB_URI!);
await client.connect();
const db = client.db("appdb");
const users = db.collection("users");
await users.insertOne({
name: "Alice",
email: "alice@example.com",
});Mongoose:
ts
import mongoose from "mongoose";
const userSchema = new mongoose.Schema({
email: { type: String, required: true, unique: true },
name: { type: String, required: true },
});
const User = mongoose.model("User", userSchema);Prisma 也支持 MongoDB,但功能模型和关系型数据库不同。使用 Prisma + MongoDB 时要认真阅读官方限制和数据建模方式,不要套用 PostgreSQL/MySQL 的关系型思维。
28. 性能和安全
性能建议:
- 高频查询必须建索引。
- 使用 projection 限制返回字段。
- 避免无限增长的大数组。
- 大分页使用游标。
- 聚合先
$match再$group。 - 用 explain 分析慢查询。
安全建议:
- 开启认证。
- 不要把数据库暴露到公网。
- 使用最小权限账号。
- 连接字符串不要提交到 Git。
- 定期备份。
- 对敏感字段加密或脱敏。
29. 常见误区
误区一:MongoDB 没有 schema,所以可以随便存。
纠正:MongoDB 是灵活 schema,不是无设计。生产集合需要明确文档结构和校验规则。
误区二:MongoDB 不支持事务,所以不能做一致性。
纠正:MongoDB 支持事务,但更推荐通过合理文档模型减少跨文档事务。
误区三:所有关系都应该嵌入。
纠正:数量无限增长、独立更新、被多处共享的数据更适合引用。
误区四:MongoDB 查询一定比 SQL 快。
纠正:性能取决于数据模型、索引、查询模式和数据量。
误区五:MongoDB 可以替代所有关系型数据库。
纠正:强关系、复杂 join、严格约束场景仍然适合 PostgreSQL/MySQL。
30. 学习路线和练习项目
第一阶段:基础 CRUD
- insertOne
- find
- updateOne
- deleteOne
- projection
第二阶段:文档建模
- 嵌入
- 引用
- 一对多
- 多对多
- 文档增长控制
第三阶段:索引
- 单字段索引
- 复合索引
- 唯一索引
- TTL 索引
- explain
第四阶段:聚合
$match$group$project$sort$lookup$unwind
第五阶段:生产
- 副本集
- 事务
- 备份
- 权限
- 性能监控
练习项目:
text
内容管理系统
- users
- articles
- categories
- comments
- audit_logs重点练习:
- 文章文档嵌入部分元数据。
- 评论数量大时拆成集合。
- 标签数组索引。
- 聚合统计阅读量。
- TTL 清理临时草稿或会话。
快速记忆清单
- MongoDB 是文档型 NoSQL 数据库,不是 SQL 数据库。
- Collection 类似表,Document 类似行,但文档可以嵌套对象和数组。
- 建模要围绕应用读写模式,而不是机械拆表。
- 一起读取、一起变化、数量有限的数据适合嵌入。
- 独立更新、共享、数量很大的数据适合引用。
- 索引对 MongoDB 同样关键,慢查询要看 explain。
- 聚合管道适合统计、转换和复杂查询。
- 支持事务不代表应该把 MongoDB 当关系型数据库使用。