主题
MongoDB 核心概念与对比
一句话:MongoDB 把一行数据换成一个可嵌套的 BSON 文档,用 schema 灵活性换建模约束,用原生分片换水平扩展;代价是跨文档一致性、复杂关联和报表能力都要单独设计。
适用版本:MongoDB 7.0 / 8.0。涉及默认值时以对应版本的官方文档为准。
为什么需要
关系库的痛点不是「不能存 JSON」,而是下面三件事同时出现时成本很高:
- 同一类实体的字段差异大(例如不同文件类型的元数据),用
ALTER TABLE加列或 EAV 模型都别扭。 - 查询模式通常是「按主键取一整条对象」,却要拆成多张表再
JOIN回来。 - 单表到十亿级后,分库分表要引入中间件,扩容逻辑和应用代码耦合。
MongoDB 的选择是:把一条完整对象存成一个文档,一次读取拿到全部;分片做进内核;schema 由应用约束,而不是数据库强制。这三件事分别对应文档模型、原生分片和可选的 schema validation。
核心机制
文档模型
- 字段可以逐条不同,但不推荐真的无 schema:生产上用
$jsonSchema校验器把必填字段和类型约束住。 - 文档是读写的最小原子单位;单文档更新天然原子,多文档原子性才需要事务。
- 内嵌减少 JOIN,但数组无限增长会撞上 16MB 上限,见 07-Schema设计模式。
存储引擎的常见误解
「MongoDB 用内存映射文件 + WAL」是 MMAPv1 时代的说法,MMAPv1 在 4.2 已移除。现在的默认引擎 WiredTiger 走的是 cache + journal + checkpoint:写操作先改内存页并记 journal,checkpoint 定期把脏页落盘。恢复语义完全不同,详见 02-存储模型与WiredTiger。
与关系库的定位对比
| 维度 | MySQL 8.0 / PostgreSQL | MongoDB 7.0 / 8.0 |
|---|---|---|
| 数据单位 | 行 + 列 | BSON 文档 |
| Schema | 强约束,DDL 变更 | 可选校验器,字段可变 |
| 关联 | JOIN,优化器成熟 | $lookup,能力有限且更贵 |
| 事务 | 成熟,默认可跨多表 | 4.0 副本集、4.2 分片,开销更高 |
| 水平扩展 | 分区 / 中间件分库分表 | 内核原生分片 |
| 聚合 | SQL | aggregation pipeline |
| 索引 | B+ 树、哈希、全文等 | B 树、复合、多键、文本、地理、TTL |
| 强项 | 事务、报表、复杂关联 | 文档读写、弹性字段、水平扩展 |
结论不是「谁替代谁」,而是:事务和复杂关联留在关系库,文档形态和水平扩展交给 MongoDB。两者并存、按主键同步,是常见架构。
查询、事务与高可用
- 查询用
find()做点查和范围查,用aggregate()做过滤、分组、关联,见 04-聚合管道与查询优化。 - 多文档事务从 4.0 起支持副本集,4.2 起支持分片;它不是默认手段,能单文档解决就不要开事务,见 06-事务与ChangeStream。
- 高可用靠副本集多数派选举,水平扩展靠 mongos + config server + shard,见 05-副本集与分片。
最小上手
bash
# MongoDB 7.0,单机验证用
docker run --name mongo -p 27017:27017 -d mongo:7.0
mongosh "mongodb://localhost:27017"javascript
// 写入两条字段不同的文档:文档模型允许,但生产应加校验器
db.products.insertMany([
{ sku: "A-1", category: "book", price: 59, pages: 320 },
{ sku: "B-1", category: "video", price: 99, duration: 5400 }
])
// 复合索引:等值字段在前,排序字段在后
db.products.createIndex({ category: 1, price: -1 })
// 验证索引是否生效:看 totalKeysExamined / totalDocsExamined
db.products.find({ category: "book" }).sort({ price: -1 })
.explain("executionStats")预期看到 winningPlan.stage 为 IXSCAN,totalKeysExamined 与返回条数接近;如果出现 COLLSCAN,说明索引没匹配上查询。
生产动作
- 写关注:MongoDB 5.0 起,PSS 副本集的隐式默认写关注是
{w:"majority"},且writeConcernMajorityJournalDefault=true时等价于带j:true;PSA(含 Arbiter)拓扑会退化为{w:1}。核心数据显式写{w:"majority", j:true, wtimeout:5000}。 - 读关注:默认
local;需要「读已多数确认」时用readConcern:"majority",但它不等于线性一致。 - 拓扑:生产至少 3 个数据节点跨可用区;Arbiter 不存数据,不能替代第二个数据节点做备份。
- 索引:按 ESR(Equal → Sort → Range)建复合索引,单集合索引不超过 64 个;用
explain("executionStats")验证。 - schema 校验:对核心集合启用
$jsonSchema,把「字段可变」限制在业务允许的范围内。 - 容量:先估算热数据能否放进 WiredTiger cache,再决定分片键和硬件;cache 默认约为
max(256MB, 50% × (RAM - 1GB))。
边界
- 复杂多表关联和报表不要硬做:
$lookup不是免费的 JOIN,数据量大时先考虑关系库或 OLAP。 - 事务不是默认选项:跨分片事务要两阶段提交,延迟和锁冲突都明显高于单文档写。
- 单文档 16MB 是硬限制:会无限增长的数组必须引用化或只保留子集。
- schema-less 不等于可以没有 schema:字段漂移会让索引和查询优化失去依据。
- 它不是缓存:热点数据仍应放 Redis,MongoDB 负责持久化和结构化查询。
讲清楚
MongoDB 的本质是用文档模型换建模灵活性。一条业务对象连同它的子结构存在一个 BSON 文档里,读一次就能拿全,不需要 JOIN;字段可以逐条不同,但生产上要用校验器把边界约束住。水平扩展是内核自带的:mongos 按分片键路由,config server 存元数据,每个 shard 本身是副本集。
它和关系库不是替代关系。事务、复杂关联、报表仍然交给 MySQL/PostgreSQL;文档形态、弹性字段、十亿级水平扩展交给 MongoDB。最常见的误区是把「schema-less」理解成「不用设计 schema」,以及把 MMAPv1 时代的内存映射文件说法套到 WiredTiger 上——后者是 cache + journal + checkpoint,恢复语义要看 checkpoint 和 journal,而不是 mmap。