【问题标题】:Better to use a document with a lot of objects or just a lot of documents?更好地使用包含大量对象或仅包含大量文档的文档?
【发布时间】:2016-09-25 02:53:56
【问题描述】:

所以我有一些公司中很多人的数据,例如他们的姓名、年龄和性别。我将把他们的信息存储在 MongoDB 中。将他们的信息存储在大量文档中或作为一堆单独的对象存储在一个文档中对我来说会更好吗?是否存在任何性能或内存问题会使一种方法优于另一种方法?

存储数据的示例方法:

大量文档

{
  _id: ObjectId('1'),
  name: 'Bart',
  age: 10,
  gender: 'Male'
},
{
  _id: ObjectId('2'),
  name: 'Lisa',
  age: 8,
  gender: 'Female'
}

一个文档中有很多对象

{
  _id: ObjectId('1'),
  'Bart': {
    age: 10,
    gender: 'Male'
  },
  'Lisa': {
    age: 8,
    gender: 'Female'
  }
}

对于任何想知道我会使用 Mongo 的投影参数查询第二个示例的人,例如

db.families.find({_id:ObjectId('1')},{_id:0,'Bart':1});

另外,我问这个的唯一原因是因为我打算在这里存储来自多家公司的人员。它们将通过集合和作为文档单独列出的人员(如第一个示例)或在文档中单独列出,并将人员单独列为公司文档中的对象。

【问题讨论】:

  • 第一个很好。 (很多文档),因为“第二个”可能会发生内存问题,并且您无法在更多字段中创建索引,因为对象名称是动态的(Bart,Lisa),并且排序也不能这样做..我的建议是第一个一个是最好的

标签: javascript mongodb optimization query-optimization


【解决方案1】:

首选第一个。

每个文档的大小限制为 16 MB。因此,将所有内容放在一个文档中更有可能遇到该障碍,您必须手动进行文档拆分,并且最终会为同一个(伪)集合提供多个文档。您需要额外的程序代码来找到正确的片段,甚至在您的应用程序中组合文档以执行一些集合级别的操作。除非有非常充分的理由这样做,否则我会不惜一切代价避免这样做。

此外,它可能最符合您的访问模式。您还有更多优化选项,例如,您可以在名称上定义索引,而对于第二个示例,您不能这样做。此外,文档越小,更新该文档的速度就越快(尤其是当无法进行就地更新时)。

如果您打算让多个公司拥有用户,您可以为每个公司使用单独的集合或在文档中添加公司属性。这取决于您要支持多少家公司,但假设它不仅仅是 2 或 3 家,我更喜欢后一种选择。它更易于维护、扩展(即分片)、优化(索引等)或扩展。

{
  _id: ObjectId('1'),
  name: 'Bart',
  age: 10,
  gender: 'Male'
  company: 'XYZ'
}

编辑

有关性能的更多注意事项。这两个选项的基本事件流程如下:

1-doc 策略(带投影)

  1. 通过 objectId 查找文档,使用索引(在内存中)快速
  2. 根据文档的大小加载整个文档(从 dics)可能会很慢
  3. 快速投影(在内存中)

n-doc 策略(无投影)

  1. 通过 objectId 或 name 查找文档,使用索引(在内存中),快速
  2. 从光盘加载(小)文档,速度慢,但比加载大文档快

特别是对于 1-doc 策略,当它变得比 n-doc 策略慢时,尤其是当文档变大时,可能会有一个临界点。对于较小的文档,它可能相等或更快,特别是当缓存开始发挥作用或发生其他边缘情况时(即名称的范围是有限的,这使得对名称的查询不是很有选择性,但在这种情况下,你会被搞砸了无论如何都是 1-doc 方法)

Mongo 对模式设计的建议如下:

  • 1:1 关系:使用嵌入文档
  • 1:few 关系:使用嵌入文档
  • 1:许多使用多个集合

你打算做的是公司:人的关系,这可能是第三或第二个选择。所以要么你有两个集合:

  • 公司
  • 人员(公司的外键)

  • 公司(嵌入人员)

无论哪种方式,我都会将人建模为

person:
{
  _id: ObjectId('1'),
  name: 'Bart',
  age: 10,
  gender: 'Male'
  company: 'XYZ' //only for foreign key relationship to separate collection
}

如果是嵌入式人员,则为公司中的数组

company:
{
  name: 'companyA',
  persons: [..] //and not use person's name as key here
}

我可以在persons.name 和/或company 上添加索引。因此搜索一个人完全在内存中运行(使用索引)并且加载个人文档应该很快,因为从磁盘中只读取一个小文档。

因此,这两种方法中的任何一种都为我提供了最高的灵活性,同时访问速度仍然非常快。

虽然在某些情况下,当投影速度很快时(可能是当有小型“公司”文档并且它们已经被缓存时),我不会那样做,因为它有一些严重的缺点(其中一些有负面的性能影响)。

  • 你不能有关于人的索引
  • 如果文档超过 16MB(最终可能会发生),您需要额外的应用程序逻辑
  • 你不能处理相同的名字(这可能会发生)
  • 您不够灵活(更改架构、在分布式环境中选择更新操作的原子性、添加其他访问模式,例如列出公司的所有人)
  • 维护可能会很麻烦(您必须检查公司文件才能找到人名)
  • 分片或复制可能会有我现在没有想到的副作用
  • 它违反了 oo 设计原则(问自己:“Bart”是一个家庭的财产还是“儿子”或更一般的“孩子”?) - 也使其难以维护

因此,即使没有证明一种方法比另一种更快,我也不会采用投影方法来过滤用户,因为到目前为止,缺点超过了(假定的)优点。

【讨论】:

  • 感谢您非常详细的回答。有几件事,我的数据不会达到 16MB 的限制,我不需要任何复杂的匹配,因为在查询它们之前我会知道名称。这对于更新来说是一个很好的观点,但是通过索引搜索内容与仅使用投影相比实际上更快吗?你能提供一个基准吗?
  • 这取决于 :) 使用按 objectId 和投影查找可能非常快,因为 objectId 上有一个索引。如果您不知道对象 ID 并且必须搜索名称,则名称上的索引会更好,尤其是对于较大的集合。如前所述,模式设计取决于应用程序的访问模式。可能是对于您的特定情况,使用单个文档会很好,但总的来说我不会那样做,尤其是使用值(名称)作为键是严重头痛的好来源;)
  • 我明白了,是的,我的对象查询将与问题中列出的完全一样;我将使用 $or 和一堆名称查询数据库,或者我将使用我的投影策略。我确切地知道我在寻找什么名字,是的,如果我走第一条路线,我会在上面使用索引。我会告诉你,如果你能证明你在说什么,使用 ObjectId 和投影更快,或者通过名称和索引搜索更快,我肯定会接受这个答案。
  • 不知道数据的定量结构很难证明。除此之外,出于其他原因(我添加到答案中),我不会推荐投影方法。结构化数据有更好的选择,这些选择也很快,并且不包括投影方法的缺点。
  • 和例子一样,我没有每个人的那么多字段,只有他们的名字,年龄和性别,这个也不会改变。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-10-29
  • 2022-01-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-01
  • 2021-02-14
相关资源
最近更新 更多