MongoDB 是 Schema-less 但不是 Design-less。正确的开发流是“访问模式驱动 → 文档结构设计 → 索引规划 → 代码实现”,而非先建表再想怎么查。请严格按以下 6 步执行:
列出所有读写场景:谁读?读什么?多频繁?是否需要实时一致性?
💡 MongoDB 的表结构完全由查询方式决定,而非实体关系。根据访问模式选择嵌入、引用或混合策略,画出文档样例。
💡 优先嵌入,除非有明确理由拆分(如无限增长、独立更新)。为每个高频查询设计索引,遵循 ESR 原则(等值-排序-范围)。
💡 没有索引的查询在生产环境等于全集合扫描。在集合级别定义字段类型、必填项和约束,防止脏数据入库。
💡 Schema Validation 是 MongoDB 的数据质量防线。用驱动或 ODM 实现业务逻辑,复杂统计用 Aggregation Pipeline。
💡 聚合管道尽量在数据库端完成,减少网络传输和应用层计算。验证每个查询是否命中预期索引,检查 nReturned vs totalDocsExamined。
💡 上线前必须对核心查询做执行计划分析。查询条件 → 索引覆盖 → 文档结构 → 验证规则 → 驱动配置 → 集群状态。
目标:理解文档模型、BSON类型、基本增删改查。
// 创建带验证规则的集合
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"
});
// 插入(单条 & 批量)
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") } });
25 默认是 double,存入后与 NumberInt(25) 不等价。务必使用 NumberInt() / NumberLong() / ISODate() 显式指定类型。
目标:掌握复杂查询运算符和 Aggregation Pipeline。
| 类别 | 运算符 | 用途 |
|---|---|---|
| 比较 | $eq, $gt, $in, $nin | 条件过滤 |
| 逻辑 | $and, $or, $not, $nor | 组合条件 |
| 数组 | $all, $elemMatch, $size | 数组元素匹配 |
| 存在性 | $exists, $type | 字段检测 |
| 正则 | $regex (加 ^ 前缀可走索引) | 模糊搜索 |
// 统计每月各标签的文章数 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 引用决策树,理解反范式设计。
| 考量维度 | ✅ 嵌入 (Embed) | ✅ 引用 (Reference) |
|---|---|---|
| 数据关系 | 一对一、一对少量 | 一对多(大量)、多对多 |
| 读取模式 | 总是同时读取 | 经常独立读取 |
| 更新频率 | 子文档随父文档一起更新 | 子文档频繁独立更新 |
| 数据大小 | < 16MB 且不无限增长 | 可能超过文档大小限制 |
| 一致性要求 | 强一致(单次写入) | 最终一致即可 |
// 订单文档:商品信息嵌入(快照),用户信息引用
{
_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")
}
目标:理解 B-Tree 索引、ESR原则、EXPLAIN 分析。
| 优先级 | 含义 | 放置位置 | 示例 |
|---|---|---|---|
| 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前,排序无法走索引
db.products.find({ category: "electronics" }).explain("executionStats");
// 核心关注点:
// winningPlan.stage: IXSCAN ✅ | COLLSCAN ❌
// executionStats.nReturned: 返回文档数
// executionStats.totalDocsExamined: 扫描文档数(应≈nReturned)
// executionStats.totalKeysExamined: 扫描索引条目数
// executionStats.executionTimeMillis: 执行耗时(ms)
// ⚠️ 健康标准:totalDocsExamined / nReturned ≤ 3
// 若比值过高,说明索引区分度不够或查询条件未充分利用索引
| 类型 | 适用场景 | 注意事项 |
|---|---|---|
| 单字段/复合索引 | 常规查询 | 遵循ESR原则 |
| 文本索引 (Text) | 全文搜索 | 每集合仅1个,权重控制相关性 |
| 地理空间索引 (2dsphere) | LBS附近搜索 | GeoJSON格式存储 |
| TTL索引 | 过期数据自动清理 | 基于date字段,后台线程60s检查 |
| 通配符索引 (Wildcard) | 动态字段查询 | MongoDB 4.2+,慎用 |
| 架构 | 节点数 | 适用场景 | 特点 |
|---|---|---|---|
| Standalone | 1 | 开发测试 | 无高可用,生产禁用 |
| Replica Set | ≥3 (P+S+A) | 绝大多数生产环境 | 自动故障转移,读写分离 |
| Sharded Cluster | Config+Mongos+Shards | 数据量>TB或写入瓶颈 | 水平扩展,运维复杂度高 |
# 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、备份恢复 |