Skip to content

文档建模

MongoDB 建模的中心问题不是“有哪些实体”,而是“哪些数据需要在同一次读写中作为一个整体”。文档边界同时影响原子性、查询次数、索引、文档增长和分片后的数据局部性。


嵌入与引用

判断维度更适合嵌入更适合引用
生命周期子数据随主对象创建和删除对象可以独立存在
读取方式经常随主对象一起读取经常独立查询或被多方使用
数量有明确上限数量可能持续无界增长
更新通常随主对象一起修改高频独立更新或多方并发更新
一致性需要单文档原子修改可以接受跨文档协调或事务
复用只属于一个聚合根被多个对象共享引用

嵌入不是越多越好。无界评论、事件、历史记录或成员数组会使文档持续膨胀,带来移动、写冲突、网络传输和大小上限风险。


冗余与一致性

文档模型常通过复制少量字段减少运行时关联。例如订单中保存下单时的商品名称和价格,是业务快照;用户昵称的冗余则可能需要在变更后同步。

设计冗余字段时应记录:

  • 数据源是谁,副本字段允许多长延迟。
  • 更新失败如何补偿,是否需要 Change Stream 或任务修复。
  • 字段是历史快照还是应与源数据保持一致。
  • 查询收益是否值得额外写放大。

常见模型模式

模式做法适用问题
Attribute把不固定属性转为 {key,value} 数组商品规格差异大且需要统一检索
Bucket将大量细粒度记录按时间或数量装入桶文档时序、事件和测量数据
Subset主文档只嵌入最常用的一小部分子数据完整集合很大但首页只读热点子集
Extended Reference在引用旁冗余目标对象的常用字段减少高频 $lookup
Computed预先维护聚合结果读远多于写的统计值
Versioning在文档中保存 Schema Version渐进迁移不同结构版本

这些模式是权衡工具,不是必须套用的模板。采用前需要写清查询、更新、数据量和一致性要求。


Schema Validation

灵活 Schema 允许渐进演进,但生产数据仍应约束关键字段的类型、必填性和取值范围:

javascript
db.createCollection("users", {
  validator: {
    $jsonSchema: {
      bsonType: "object",
      required: ["username", "enabled", "createdAt"],
      properties: {
        username: { bsonType: "string", minLength: 1 },
        enabled: { bsonType: "bool" },
        balance: { bsonType: "decimal" },
        createdAt: { bsonType: "date" }
      }
    }
  },
  validationLevel: "strict",
  validationAction: "error"
})

Validation 不会自动把旧数据转换成新结构。规则上线前应先扫描历史数据,决定拒绝、告警、迁移或兼容多个 Schema Version。


建模评审问题

  1. 最常见的查询是否可以用一个文档或少量索引完成。
  2. 单文档是否可能持续增长到不可控。
  3. 哪些写入必须原子,哪些可以最终一致。
  4. 数组是否会成为并发更新热点或产生大量 Multikey Index Entry。
  5. 未来 Shard Key 是否能从模型中稳定提取。
  6. 字段类型和缺失、null 的语义是否一致。

参考资料