跳到正文

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 / PostgreSQLMongoDB 7.0 / 8.0
数据单位行 + 列BSON 文档
Schema强约束,DDL 变更可选校验器,字段可变
关联JOIN,优化器成熟$lookup,能力有限且更贵
事务成熟,默认可跨多表4.0 副本集、4.2 分片,开销更高
水平扩展分区 / 中间件分库分表内核原生分片
聚合SQLaggregation 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。

把一门技术讲透到既懂也会用