文档建模的核心判断
文档库建模的核心是"一次查询要拿到的数据放在一起":一对一与小规模一对多适合内嵌,无界增长与多对多适合引用。内嵌能一次读取到位、单文档内更新具备原子性;引用的代价是应用层要做拼装或多查一次。
怎么判断增长边界
判断依据是查询模式与增长边界:读多写少、数量可预期、随主体生命周期存在的数据适合内嵌;写多、数量无界、被多处引用的拆成独立集合。同时要留意单文档有大小上限,数组持续追加会让文档变大并在磁盘上搬迁,写放大非常明显。
基本信息
| 内嵌 | 读一次到位,更新原子性限于单文档 |
|---|---|
| 引用 | 通过关联字段查询,需要在应用层拼装 |
| 单文档上限 | 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" } }
])典型使用场景
- 用户资料:地址、偏好等随用户存在的数据内嵌
- 订单与明细:明细数量可控就内嵌,无界增长就拆开
- 多对多关系:标签、权限这类被多处引用的数据独立成集合
- 读写比判断:读远多于写时倾向内嵌,减少查询次数
- 迁移评估:从关系模型迁移时先梳理查询清单,而不是照抄表结构
场景与推荐方式
| 场景 | 推荐方式 | 理由 |
|---|---|---|
| 一对一 | 内嵌 | 一次读取到位 |
| 小规模一对多 | 内嵌数组 | 读取简单,更新原子 |
| 无界一对多 | 引用 | 避免文档无限增长 |
| 多对多 | 引用 | 两侧都可能被独立查询 |
| 高频更新的子文档 | 引用或拆分 | 减少整篇文档重写 |
判断标准是查询模式与增长边界:读多写少、数量可控的内嵌;写多、数量无界、被多处引用的拆出去,同时留意单文档大小上限。
内嵌数组持续追加会让文档变大并在磁盘上搬迁;如果某个数组没有天然上限(例如日志、事件明细),设计阶段就应该拆成独立集合。