首页/数据/NoSQL 速查/文档建模:内嵌与引用取舍
选型与架构50 条

文档建模:内嵌与引用取舍

文档库建模的核心是"一次查询要拿到的数据放在一起":一对一与小规模一对多适合内嵌,无界增长与多对多适合引用;数组无序增长会带来重写与迁移成本。

NoSQL 速查数据更新于 2026-10-13

文档建模的核心判断

文档库建模的核心是"一次查询要拿到的数据放在一起":一对一与小规模一对多适合内嵌,无界增长与多对多适合引用。内嵌能一次读取到位、单文档内更新具备原子性;引用的代价是应用层要做拼装或多查一次。

怎么判断增长边界

判断依据是查询模式与增长边界:读多写少、数量可预期、随主体生命周期存在的数据适合内嵌;写多、数量无界、被多处引用的拆成独立集合。同时要留意单文档有大小上限,数组持续追加会让文档变大并在磁盘上搬迁,写放大非常明显。

基本信息

内嵌读一次到位,更新原子性限于单文档
引用通过关联字段查询,需要在应用层拼装
单文档上限MongoDB 为 16MB,数组过大触发搬迁与写放大
判断依据查询模式、增长边界、是否被多处引用
适用范围文档型数据库通用思路,以 MongoDB 为例

示例

示例
// 内嵌:地址随用户生命周期存在,读用户即得
{ _id: 1, name: "ann", addresses: [ { city: "hz", zip: "310000" } ] }

// 引用:订单量无界,独立集合加外键字段
// orders: { _id: 1001, userId: 1, items: [ { sku: "a", qty: 2 } ] }

// 需要拼装时用 $lookup(代价是额外查询与内存)
db.orders.aggregate([
  { $match: { status: "paid" } },
  { $lookup: { from: "users", localField: "userId", foreignField: "_id", as: "user" } }
])

典型使用场景

  • 用户资料:地址、偏好等随用户存在的数据内嵌
  • 订单与明细:明细数量可控就内嵌,无界增长就拆开
  • 多对多关系:标签、权限这类被多处引用的数据独立成集合
  • 读写比判断:读远多于写时倾向内嵌,减少查询次数
  • 迁移评估:从关系模型迁移时先梳理查询清单,而不是照抄表结构

场景与推荐方式

场景推荐方式理由
一对一内嵌一次读取到位
小规模一对多内嵌数组读取简单,更新原子
无界一对多引用避免文档无限增长
多对多引用两侧都可能被独立查询
高频更新的子文档引用或拆分减少整篇文档重写
判断标准是查询模式与增长边界:读多写少、数量可控的内嵌;写多、数量无界、被多处引用的拆出去,同时留意单文档大小上限。
内嵌数组持续追加会让文档变大并在磁盘上搬迁;如果某个数组没有天然上限(例如日志、事件明细),设计阶段就应该拆成独立集合。

易错点

  • 内嵌数组持续追加会让文档变大并在磁盘上搬迁,写放大明显
  • 完全照抄关系模型(全部拆表)会失去文档库读取一次到位的优势
  • 忽视单文档大小上限,数据增长到临界值后写入会直接失败
  • 用大量 $lookup 拼装代替合理内嵌,会让查询延迟与内存开销同步上升

数据来源与参考

  • MongoDB Manual: Data modeling
  • MongoDB Manual: Model one-to-many relationships
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌