Docs 菜单
Docs 主页
/
MongoDB Ops Manager
/ /

减少$lookup 操作

在此页面上

  • 概述
  • 举例
  • 非规范化
  • 了解详情

$lookup操作根据指定字段连接同一数据库中两个集合的数据。 当您的数据结构类似于关系数据库并且需要对大型分层数据集进行建模时, $lookup操作会非常有用。 但是,这些操作可能很慢并且会耗费大量资源,因为它们需要读取两个集合(而不是单个集合)并对其执行逻辑。

如果您经常运行$lookup操作,请考虑重组您的模式,以便您的应用程序可以查询单个集合以获得所需的所有信息。 您可以利用 MongoDB灵活的模式模型以及嵌入式文档和数组来捕获单个文档结构中的数据之间的关系。 使用这种非规范化模型,可以充分利用 MongoDB 丰富的文档,并允许您的应用程序在单个查询中检索和操作相关数据。

以下示例展示了两种旨在减少$lookup操作的模式结构:

请考虑以下示例,一家杂货店跟踪两个单独集合中一对一的库存和营养信息。每个库存商品对应唯一的营养成分商品。nutrition_id 字段将 inventory 集合链接到 nutrition_facts 集合,类似于表格数据库:

// inventory collection
{
"name": "Pear",
"stock": 20,
"nutrition_id": 123, // reference to a nutrition_fact document
...
}
{
"name": "Candy Bar",
"stock": 26,
"nutrition_id": 456,
...
}
// nutrition_facts collection
{
"_id": 123,
"calories": 100,
"grams_sugar": 17,
"grams_protein": 1,
...
}
{
"_id": 456,
"calories": 250,
"grams_sugar": 27,
"grams_protein": 4,
...
}

如果应用程序通过name请求库存列项的营养成分,则此模式结构需要nutrition_facts集合的 { $lookup } 来查找与库存列项的nutrition_id匹配的条目。

相反,您可以将营养信息嵌入 inventory 集合中:

// inventory collection
{
"name": "Pear",
"stock": 20,
"nutrition_facts": {
"calories": 100,
"grams_sugar": 17,
"grams_protein": 1,
...
}
...
}
{
"name": "Candy Bar",
"stock": 26,
"nutrition_facts": {
"calories": 250,
"grams_sugar": 27,
"grams_protein": 4,
...
}
...
}

这样,当您查询 inventory 中的商品时,结果中就会包含营养成分,而无需再次查询或执行 $lookup 操作。当跨集合数据具有一对一的关系时,可考虑嵌入文档。

请考虑以下示例,棒球联盟的 players 集合中的文档引用了 teams 集合中的文档,类似于表格数据库:

// players collection
{
"team_id": 1, // reference to a team document
"name": "Nick",
"position": "Pitcher"
...
}
{
"team_id": 1,
"name": "Anuj",
"position": "Shortstop"
...
}
// teams collection
{
"_id": 1,
"name": "Danbury Dolphins"
...
}

如果应用程序请求球队中的球员列表,则此模式结构需要players集合的$lookup } 来查找与team_id匹配的每个球员。

相反,您可以列出球队文档本身的数组中的 players

// teams collection
{
"_id": 1,
"name": "Danbury Dolphins",
"players": [
{
"name": "Nick",
"position": "Pitcher"
...
},
{
"name": "Anuj",
"position": "Shortstop"
...
}
]
}

通过利用数组来保存相关数据,应用程序可以检索完整的 team 信息,包括球队球员,而无需对其他集合执行 $lookup 操作或创建索引。在这种情况下,使用数组比将信息存储在单独的集合中有着更好的性能。

注意

以上示例中,棒球队球员人数是固定的, 不存在数组变得过大的风险。

读取和写入大型数组的性能成本可能超过避免$lookup操作所获得的好处。 如果您的数组无限制或非常大,这些数组可能会降低读写性能。

如果创建数组索引,数组中的每个元素都会被索引。 如果经常写入该数组, 索引或重新索引一个潜在的大数组字段的性能成本可能会很高。

提示

另请参阅:

避免无界数组

非规范化模式是指复制字段或从现有字段派生新字段的过程。 非规范化 可以在多种情况下提高读取性能,例如:

  • 重复查询需要另一个集合中大型文档的几个字段。您可以选择在重复查询所针对的集合的嵌入式文档中维护这些字段的副本,以避免合并两个不同的集合或频繁执行 $lookup 操作。

  • 经常请求集合中某个字段的平均值。 您可以选择在单独的集合中创建派生字段, 在写入过程中予以更新,并保持该字段的运行平均值。

虽然对相关数据分组时首选嵌入没有重复的文档或数组, 但在必须维护单独的集合时, 非规范化可以提高读取性能。

注意

非规范化模式时,维护一致的重复数据就成了您的责任。

模式的最佳结构取决于应用程序环境。 以下资源提供了有关数据建模的详细信息,以及嵌入式文档和数组的其他示例用例:

要了解如何将灵活数据模型整合到您的架构中,请参阅 MongoDB.live 2020 中的以下演示文稿:

要了解有关如何在 MongoDB 中查询数组的更多信息,请参阅查询数组。

要了解数组运行良好的情况,请参阅以下设计模式:

  • 使用属性模式处理具有唯一属性组合的数据,例如每部电影在一个国家/地区子集上映的电影数据。

  • 使用存储桶模式处理紧密分组或连续的数据,例如时间跨度数据。

  • 使用多态模式处理同一集合中不同形状的文档,例如多个运动项目的运动员记录。

如需了解复制数据可优化模式的情况, 请参阅以下设计模式:

  • 使用扩展引用模式将大型文档中经常读取的数据部分复制到较小的文档中。

后退

改进您的架构