首页 / MongoDB 入门教程 / 嵌入 vs 引用:两种建模方式

MongoDB 入门教程

嵌入 vs 引用:两种建模方式

本教程共 50 篇 · 第 36 篇 · 更新于 2026-07-30 · 约 4 分钟阅读

MongoDBMongoDB 入门教程数据建模嵌入引用原子性

36. 嵌入 vs 引用:两种建模方式

本节目标:搞懂 MongoDB 两种关联数据的方式,学会在「嵌入」和「引用」之间做正确取舍。

关系型数据库喜欢把数据拆成多张表,再用 JOIN 拼回来。MongoDB 走了另一条路:它允许你把数据塞进同一个文档(Document),也能像传统方式那样用 ID 去关联别的集合(Collection)。这两种做法,就是嵌入与引用。

36.1 嵌入:把数据放在一个文档里

嵌入(Embedding)就是在一个文档内部,用内嵌文档或数组把相关数据一起存下来。比如用户的收货地址,直接写进用户文档里。

// 把地址嵌入用户文档
test> db.users.updateOne(
  { _id: 1 },
  { $set: {
      profile: { phone: "13800000000", bio: "爱折腾的后端" }
  }}
)

这样取用户时,连地址一起拿到了,一次读取就够。我之前踩过的坑,就是过早把什么都拆表,结果一个页面要查五六次库。

Tip

一句话口诀:经常一起读的数据,就放在一起。这能省掉大量跨集合查询。

36.2 引用:用 ID 跨集合关联

引用(Referencing)则是把数据放另一个集合,用 _id 互相指。比如订单存在 orders 集合,只记一个 user_id 指向 users

// 订单引用用户,而不是把用户信息抄进来
test> db.orders.insertOne({
  user_id: 1,
  items: [{ product: "键盘", qty: 1, price: 199 }],
  total: 199,
  status: "paid",
  created_at: new Date()
})

查的时候先拿订单,再用 user_idusers 找用户。这种写法避免了重复存储,但要多查一次。

Note

引用本质就是「我在别处,凭 ID 来找我」。它和关系库的「外键」思路很像,只是 MongoDB 不强制约束。

36.3 怎么选:读多写少 vs 需独立访问

选嵌入还是引用,看数据的访问模式,而不是看「关系是一对多还是多对多」。

优先用嵌入,当数据满足这几点:

  • 总是和用户一起被读取。
  • 数据量小、不会无限增长。
  • 写操作以「整块更新」为主。

优先用引用,当数据满足这几点:

  • 需要被独立访问,比如订单要单独查、单独分页。
  • 一方会 unbounded 增长,比如一个用户的上千条订单。
  • 同一份数据被多处引用,复制一份会很难维护。
Warning

数组不是无限大的。MongoDB 单个文档上限 16MB。把会无限增长的子数据嵌入,迟早撑爆文档。

36.4 原子性边界:单文档操作天然安全

MongoDB 保证「单个文档内的写操作是原子的」。也就是说,你在一个文档里改嵌入地址和改用户名,要么都成功,要么都失败。

// 单文档更新是原子的,嵌入数据一起变
test> db.users.updateOne(
  { _id: 1 },
  { $set: { name: "小明", "profile.phone": "13900000000" } }
)

但跨文档、跨集合就不天然原子了。比如「扣库存」和「建订单」分属两个集合,单条命令无法保证两者同时成功。

Note

正因为单文档原子,很多场景下用嵌入就能躲开事务的复杂度。后面第 49 章会讲,跨文档才需要多文档事务。

36.5 小结:建模是取舍,不是对错

嵌入带来快读和原子性,代价是数据可能冗余。引用带来灵活和独立,代价是多一次查询、失去天然原子性。没有标准答案,只有适合你访问模式的答案。

下三章我们拆开看:一对一、一对多、多对多,分别该怎么建。