Skip to content

MongoDB - 数据建模

MongoDB 中的数据具有灵活的模式(schema)。与传统 SQL 数据库不同,同一个集合中的文档不必具有相同的字段集或结构。这种灵活性是一个强大的特性,但精心设计的数据模型对于应用程序的性能、可扩展性和可维护性至关重要。MongoDB 数据建模的关键在于根据应用程序的查询和更新需求来构建数据。

MongoDB 的文档模型允许您以两种主要方式构建数据:将相关数据嵌入到单个文档中,或在不同集合之间引用数据。

在此模型中,相关数据嵌套在单个文档内。这也被称为非范式化(denormalization)。这种方法允许您在单个数据库查询中检索所有相关信息,效率非常高。

用例: 一个包含用户地址信息的 user 文档。由于地址本质上是用户个人资料的一部分,并且几乎总是与用户一起查询,因此嵌入是一个很好的选择。

{
"_id": ObjectId("64f7c7f8b3e9a1e8a8b1a8d9"),
"username": "jane.doe",
"email": "jane.doe@example.com",
"address": {
"street": "123 Main St",
"city": "Anytown",
"state": "CA",
"zip": "12345"
}
}

优点: 所有数据都在一个操作中检索,读取速度更快。在单个文档上的操作是原子性的。

缺点: 如果嵌入的信息也需要单独使用,可能导致大型文档和数据重复。

在此模型中,您通过在单独的集合中存储引用(通常是相关文档的 _id 字段)来维护文档之间的关系。这类似于传统的_关系型数据库_模型,当相关数据量大、频繁更新或独立使用时,这种方法很有用。

用例: 一个包含 products(产品)和 orders(订单)的电子商务应用程序。每个 order 文档都引用其包含的 products。这避免了在每个订单中重复产品信息。

产品集合 (Products Collection):

{
"_id": ObjectId("A123"),
"name": "Wireless Mouse",
"price": 29.99,
"stock": 150
}
{
"_id": ObjectId("B456"),
"name": "Mechanical Keyboard",
"price": 89.99,
"stock": 75
}

订单集合 (Orders Collection):

{
"_id": ObjectId("ORDER_001"),
"userId": ObjectId("USER_XYZ"),
"orderDate": ISODate("2023-09-06T10:00:00Z"),
"items": [
{ "productId": ObjectId("A123"), "quantity": 2 },
{ "productId": ObjectId("B456"), "quantity": 1 }
]
}

优点: 减少数据重复,更容易管理频繁更改的相关数据。

缺点: 需要单独的查询($lookup 聚合阶段)来检索相关数据,这可能比读取嵌入式文档慢。

  • 关系类型: 对于“一对少量”(one-to-few)关系(例如,用户及其地址),使用嵌入。对于“一对多”(one-to-many)(例如,产品及其部件)或“多对多”(many-to-many)关系(例如,学生和课程),使用引用。
  • 数据访问模式: 设计您的模式以支持您最频繁的查询。如果您总是需要将数据一起使用,请将其嵌入。如果您单独访问它,请引用它。
  • 原子性: 单个文档上的操作是原子性的。如果您需要对相关数据进行原子读/写操作,嵌入是首选模型。
  • 数据重复: 如果能显著提高读取性能,一些重复是可以接受的。磁盘空间通常比计算时间便宜。
  • 聚合的模式: 对于复杂的报告,您可能需要以最适合_聚合框架_的方式构建数据。
  • 索引: 您的数据模型将影响您的索引策略。确保您的模式允许在频繁查询的字段上建立高效索引。

让我们为一篇博客文章建模,它包含标题、内容、作者、标签和评论。评论是特定于文章的,并且通常与文章一起显示。这使其成为嵌入式模型的首选。

在关系型(SQL)数据库中,这通常需要至少三张表(posts、comments、tags)在查询时进行连接。在 MongoDB 中,我们可以用一个直观的文档来建模。

{
"_id": ObjectId("POST_ID_123"),
"title": "我的第一篇 MongoDB 文章",
"content": "这是博客文章的正文...",
"author_id": ObjectId("AUTHOR_ID_456"), // 作者ID
"author_name": "John Smith", // 为了性能而重复
"url_slug": "my-first-mongodb-post", // URL别名
"tags": ["mongodb", "database", "nosql"],
"likes": 150,
"published_at": ISODate("2023-09-01T14:30:00Z"), // 发布时间
"comments": [ // 评论列表
{
"user_id": ObjectId("USER_ID_789"), // 用户ID
"username": "评论者一号",
"message": "好文章!非常有帮助。", // 消息内容
"createdAt": ISODate("2023-09-02T11:00:00Z"), // 创建时间
"likes": 12 // 点赞数
},
{
"user_id": ObjectId("USER_ID_101"),
"username": "另一个读者",
"message": "谢谢分享。",
"createdAt": ISODate("2023-09-03T16:45:00Z"),
"likes": 5
}
]
}

这种单文档模型意味着当用户查看博客文章时,所有必要的信息(包括其所有评论)都可以通过一次快速的读取操作来检索,从而提供卓越的用户体验。