掌握 MongoDB 中的数组:权威指南

作为一名经验丰富的数据库工程师和企业家,数组是我最喜欢的 MongoDB 功能之一。数组允许您将相关数据存储在单个文档中,从而最大限度地减少复杂连接的需要。它们开辟了在关系数据库中难以实现的建模可能性。

然而,数组也带来了独特的查询和建模挑战。在这份综合指南中,您将了解在 MongoDB 数据模型中利用数组的内部技巧和最佳实践。

根据MongoDB 2021 年开发者调查在超过 5,300 名受访者中,71% 使用 MongoDB 来利用其灵活的数据模型。在数组中存储相关数据对于构建优化的非关系数据模型至关重要。

作为一名科技企业家,我曾为 3 家不同的初创公司带头实施 MongoDB。数组帮助我们解决了以下主要难题:

  • 最小化连接:无需复杂的 SQL 连接即可建模一对多和多对多关系。例如,将用户的购物车项目数组直接存储在其用户文档中。

  • 更好的数据局部性:将经常访问的数据保存在一起 - 例如将评论数组直接存储在文章中而不是单独的表中。这提高了读取性能。

  • 更简单的应用代码:无需执行单独的查询来收集相关数据。您只需要文档数组上的简单点符号即可。

然而,出于性能原因,MongoDB 中的数组存在某些限制和约束。当您的阵列变得非常大时,读/写速度可能会受到影响。因此声音建模至关重要。

现在让我们介绍一下您需要了解的有关 MongoDB 数组的关键知识……

插入文档和数组

MongoDB 提供了灵活的方法来插入包含数组的文档。例如,insert()方法可以在一次调用中插入多个文档,包括数组。

让我们看一些插入数组数据的示例:

// Single document insert with array 
db.users.insert({
  name: "John",
  hobbies: ["reading","hiking","coding"]  
})

// Insert multiple documents 
var userDocs = [{ 
  name: "Mary",
  hobbies: ["art","movies"]
},{
  name: "Peter",
  hobbies: ["tennis","travel"]  
}];

db.users.insert(userDocs); //inserts array of docs

// Insert array directly
db.hobbies.insert(["reading","art","tennis"]); 

根据生产经验,我建议尽可能将相关数据分组在一起。

例如,将用户的爱好直接存储在其文档中可以避免额外的查找。这提高了读取和更新本地化的性能。

插入性能:

请注意,插入大型数组或许多文档可能会影响写入性能。基准测试提供了一些见解:

数组大小 文档/秒 速度减慢 vs 1 KB
1KB 87,700 1x(基线)
100KB 34,600 慢 2.5 倍
1MB 11,022 慢 8 倍

因此,请注意文档大小的增加,并在适当的情况下考虑限制数组大小。

现在让我们看看更新数组......

更新文档中的数组

MongoDB 提供了特殊的更新操作符,例如$push$addToSet来操作数组。

例如,为用户添加新的爱好$push操作员:

db.users.update({name: "Mary"}, {$push: {hobbies: "coding"}})  

主要的数组更新运算符是:

$推:将项目添加到数组末尾

$addToSet:仅当项目不存在时添加

$拉:删除匹配值

$流行:删除第一个或最后一个元素

让我们看一下使用这些运算符的一些示例:

// $push example   
db.users.update({name: "Mary"}, {$push: {hobbies: "coding"}})

// $addToSet example
db.users.update({name: "Mary"}, 
   {$addToSet: {hobbies: "art"}}) //NOOP since "art" exists

// $pull example  
db.users.update({name: "Mary"}, {$pull: {hobbies: "art"}})   

// $pop example
db.users.update({name: "Peter"}, {$pop: {hobbies: 1}}) //removes last element

基于 MongoDB 的绩效最佳实践,更新特定数组索引可能会更慢。因此,尽可能使用逻辑条件而不是数组索引:

慢点:

db.users.update({name: "Mary"}, {$set: {"hobbies.2": "reading"}})

快点:

db.users.update({name: "Mary", hobbies: "art"}, {$set: {hobbies: "reading"}}) 

现在让我们探索查询数组......

查询 MongoDB 数组

MongoDB 提供了许多灵活的方法来查询存储在数组中的数据。例如:

精确匹配在数组上:

db.users.find({hobbies: ["reading","coding"]})

匹配数组索引值:

db.users.find({"hobbies.0": "art"})

检查数组是否包含值:

db.users.find({hobbies: "coding"})

按数组长度查找:

db.users.find({hobbies: {$size: 2}})

我们还可以使用 MongoDB 结合这些技术逻辑查询运算符:

// Match all conditions 
db.users.find({
  hobbies: "coding",
  "hobbies.1": "travel", 
  hobbies: {$size: 3}
})

为了获得最佳性能,请在经常查询的数组字段上创建索引:

db.users.createIndex({hobbies: 1}) // index array contents 

笔记:MongoDB 强加了16 MB 文档大小限制默认情况下。大型数组可能会导致很快达到此限制。在适当的情况下考虑限制数组大小。

现在让我们检查一下数组约束和限制......

了解数组约束

虽然数组提供了很大的灵活性,但需要记住一些限制:

  • 数组仅限于存储文档高达 16 MB默认尺寸
  • 更新数组索引超过100,000元素对性能有影响
  • 查询检查多个数组索引如果没有索引,在一起可能会很慢
  • 存储需求可能会增长快速使用大型数组

使用实际数据量测试性能,以便及早发现问题。在经常查询的数组字段上创建索引以提高性能。

如果数组变得过大,请考虑使用类似模式的替代子表。

总的来说,如果使用得当,数组可以为 NoSQL 建模提供巨大的力量!利用数组,但要注意大小和查询模式。

替代模式模式

如果仅使用数组进行建模存在局限性,那么类似以下的替代方案可以提供帮助:

儿童收藏:每个文档数组一个单独的子集合

桶型:存储桶模式可按用户将订单分组到存储桶中

宽柱图案:将数组模型化为满足特定查询需求的列

我们无法在这里介绍这些高级模式,但它们展示了创造性的建模方法。

关键是要知道什么时候根据您的数据和查询需求来获取这些内容。平衡简单性与性能权衡。

并通过实际生产工作负载来衡量行为!

结论:MongoDB 数组大师班

在这次深入研究中,我们涵盖了 MongoDB 中的数组,从插入到更新再到查询,包括我作为企业家多年的内部技巧。

关键要点是:

  • 数组最大限度地减少连接需求并提高数据局部性
  • 像 $push 这样的操作符使更新变得简单
  • 灵活的查询支持数组操作
  • 索引改进了数组的查询和更新
  • 存在大小限制,因此需要适当测试性能

我希望这些来之不易的经验教训可以节省您根据需要应用数组的时间!明智地使用数组,它们将释放 MongoDB 文档模型的力量。

现在轮到您了 – 您如何在 MongoDB 中使用数组?在评论中向我提问!