MongoDB 从 0 到 1 完整教程

文档模型设计 · 聚合管道 · 索引优化 · 生产级运维规范

📑 学习导航

🗺️ 新手必读:MongoDB 开发操作顺序

MongoDB 是 Schema-less 但不是 Design-less。正确的开发流是“访问模式驱动 → 文档结构设计 → 索引规划 → 代码实现”,而非先建表再想怎么查。请严格按以下 6 步执行:

Step 1: 梳理访问模式

📝 Access Pattern Document

列出所有读写场景:谁读?读什么?多频繁?是否需要实时一致性?

💡 MongoDB 的表结构完全由查询方式决定,而非实体关系。

Step 2: 设计文档结构

📁 Schema Design

根据访问模式选择嵌入、引用或混合策略,画出文档样例。

💡 优先嵌入,除非有明确理由拆分(如无限增长、独立更新)。

Step 3: 规划索引策略

⌨️ Index Plan

为每个高频查询设计索引,遵循 ESR 原则(等值-排序-范围)。

💡 没有索引的查询在生产环境等于全集合扫描。

Step 4: 编写验证规则

⌨️ $jsonSchema Validator

在集合级别定义字段类型、必填项和约束,防止脏数据入库。

💡 Schema Validation 是 MongoDB 的数据质量防线。

Step 5: 实现 CRUD + 聚合

📁 Application Code

用驱动或 ODM 实现业务逻辑,复杂统计用 Aggregation Pipeline。

💡 聚合管道尽量在数据库端完成,减少网络传输和应用层计算。

Step 6: EXPLAIN 验证 + 监控

⌨️ explain("executionStats")

验证每个查询是否命中预期索引,检查 nReturned vs totalDocsExamined。

💡 上线前必须对核心查询做执行计划分析。
⚠️ 黄金法则: Data is accessed together → store together. 查询模式决定文档结构,而非 ER 图。
🔍 排错顺序: 查询条件 → 索引覆盖 → 文档结构 → 验证规则 → 驱动配置 → 集群状态

🟢 第一阶段:核心概念与基础 CRUD

目标:理解文档模型、BSON类型、基本增删改查。

1. 集合创建与 Schema Validation

// 创建带验证规则的集合
db.createCollection("users", {
  validator: {
    $jsonSchema: {
      bsonType: "object",
      required: ["username", "email", "createdAt"],
      properties: {
        username: { bsonType: "string", minLength: 3 },
        email: { bsonType: "string", pattern: "^.+@.+$" },
        age: { bsonType: "int", minimum: 0, maximum: 150 },
        tags: { bsonType: "array", items: { bsonType: "string" } },
        createdAt: { bsonType: "date" }
      }
    }
  },
  validationLevel: "strict",
  validationAction: "error"
});

2. 基础 CRUD 操作

// 插入(单条 & 批量)
db.users.insertOne({
  username: "alice",
  email: "alice@example.com",
  age: NumberInt(25),
  tags: ["frontend", "react"],
  createdAt: new Date()
});

// 查询
db.users.find({ age: { $gte: 20 }, tags: "react" })
  .sort({ createdAt: -1 })
  .limit(10);

// 更新(原子操作)
db.users.updateOne(
  { username: "alice" },
  { $set: { age: 26 }, $push: { tags: "nextjs" } }
);

// 删除
db.users.deleteMany({ createdAt: { $lt: ISODate("2023-01-01") } });
⚠️ BSON 类型陷阱: JavaScript 中 25 默认是 double,存入后与 NumberInt(25) 不等价。务必使用 NumberInt() / NumberLong() / ISODate() 显式指定类型。

🟩 第二阶段:高级查询与聚合管道

目标:掌握复杂查询运算符和 Aggregation Pipeline。

1. 常用查询运算符

类别运算符用途
比较$eq, $gt, $in, $nin条件过滤
逻辑$and, $or, $not, $nor组合条件
数组$all, $elemMatch, $size数组元素匹配
存在性$exists, $type字段检测
正则$regex (加 ^ 前缀可走索引)模糊搜索

2. 聚合管道实战

// 统计每月各标签的文章数 Top5
db.articles.aggregate([
  // Stage 1: 过滤时间范围
  { $match: { createdAt: { $gte: ISODate("2024-01-01") } } },
  
  // Stage 2: 展开数组(一篇多标签变多条记录)
  { $unwind: "$tags" },
  
  // Stage 3: 分组统计
  { $group: {
      _id: { month: { $month: "$createdAt" }, tag: "$tags" },
      count: { $sum: 1 }
  }},
  
  // Stage 4: 按月份分组取Top5
  { $sort: { "_id.month": 1, count: -1 } },
  { $group: {
      _id: "$_id.month",
      topTags: { $push: { tag: "$_id.tag", count: "$count" } }
  }},
  { $project: {
      month: "$_id",
      topTags: { $slice: ["$topTags", 5] }
  }}
]);
💡 聚合管道优化原则: $match$sort 尽量放在管道最前面以利用索引;$project 尽早裁剪字段减少内存占用;避免在 $group 中使用高基数字段导致内存溢出。

🔵 第三阶段:数据建模与范式权衡

目标:掌握嵌入 vs 引用决策树,理解反范式设计。

1. 嵌入 vs 引用 决策矩阵

考量维度✅ 嵌入 (Embed)✅ 引用 (Reference)
数据关系一对一、一对少量一对多(大量)、多对多
读取模式总是同时读取经常独立读取
更新频率子文档随父文档一起更新子文档频繁独立更新
数据大小< 16MB 且不无限增长可能超过文档大小限制
一致性要求强一致(单次写入)最终一致即可

2. 混合模式示例:订单系统

// 订单文档:商品信息嵌入(快照),用户信息引用
{
  _id: ObjectId("..."),
  userId: ObjectId("..."),          // 引用:用户信息独立且频繁更新
  items: [                          // 嵌入:下单时商品快照,不应随商品修改而变化
    { productId: ObjectId("..."), name: "机械键盘", price: 599, qty: 1 },
    { productId: ObjectId("..."), name: "鼠标垫", price: 49, qty: 2 }
  ],
  shippingAddress: { ... },         // 嵌入:地址快照
  status: "paid",
  createdAt: ISODate("2024-12-01T10:00:00Z")
}
💡 建模铁律: 不要照搬关系型数据库的三范式。MongoDB 的优势在于将关联数据物理存储在一起,减少 JOIN。如果你发现自己总是在应用层做 "JOIN",说明模型设计有问题。

🔴 第四阶段:索引原理与性能调优

目标:理解 B-Tree 索引、ESR原则、EXPLAIN 分析。

1. ESR 索引设计原则

优先级含义放置位置示例
Equality等值匹配字段索引最左侧{ status: "active" }
Sort排序字段等值字段之后{ createdAt: -1 }
Range范围查询字段索引最右侧{ price: { $gte: 100 } }
// 查询: db.products.find({ category: "electronics", price: { $gte: 100 } }).sort({ createdAt: -1 })
// ✅ 正确索引 (ESR): { category: 1, createdAt: -1, price: 1 }
// ❌ 错误索引: { category: 1, price: 1, createdAt: -1 }  ← range在sort前,排序无法走索引

2. EXPLAIN 关键指标解读

db.products.find({ category: "electronics" }).explain("executionStats");

// 核心关注点:
// winningPlan.stage:       IXSCAN ✅ | COLLSCAN ❌
// executionStats.nReturned:          返回文档数
// executionStats.totalDocsExamined:  扫描文档数(应≈nReturned)
// executionStats.totalKeysExamined:  扫描索引条目数
// executionStats.executionTimeMillis: 执行耗时(ms)

// ⚠️ 健康标准:totalDocsExamined / nReturned ≤ 3
// 若比值过高,说明索引区分度不够或查询条件未充分利用索引

3. 索引类型速查

类型适用场景注意事项
单字段/复合索引常规查询遵循ESR原则
文本索引 (Text)全文搜索每集合仅1个,权重控制相关性
地理空间索引 (2dsphere)LBS附近搜索GeoJSON格式存储
TTL索引过期数据自动清理基于date字段,后台线程60s检查
通配符索引 (Wildcard)动态字段查询MongoDB 4.2+,慎用

🟣 第五阶段:副本集、分片与安全加固

1. 部署架构选型

架构节点数适用场景特点
Standalone1开发测试无高可用,生产禁用
Replica Set≥3 (P+S+A)绝大多数生产环境自动故障转移,读写分离
Sharded ClusterConfig+Mongos+Shards数据量>TB或写入瓶颈水平扩展,运维复杂度高

2. 生产安全清单

# mongod.conf 关键配置
security:
  authorization: enabled           # ✅ 必须开启认证
  javascriptEnabled: false         # ✅ 禁用服务端JS执行
  
net:
  bindIp: 127.0.0.1,10.0.0.5      # ✅ 禁止绑定 0.0.0.0
  tls:
    mode: requireTLS               # ✅ 强制TLS加密
    certificateKeyFile: /etc/mongo/server.pem
    CAFile: /etc/mongo/ca.pem

operationProfiling:
  slowOpThresholdMs: 100           # ✅ 慢查询阈值
  mode: slowOp                     # ✅ 记录慢查询日志
🚫 生产环境绝对禁止:

📚 核心知识体系总结

阶段核心关键词重点掌握内容
基础BSON, CRUD, Validation文档结构、类型安全、原子更新运算符
查询Aggregation, Pipeline$match/$group/$lookup、管道优化
建模嵌入vs引用, 访问模式反范式设计、文档大小限制、快照策略
索引ESR, EXPLAIN, B-Tree复合索引设计、执行计划分析、索引类型
运维Replica Set, Security副本集选举、RBAC权限、TLS、备份恢复