MongoDB嵌入式文档与引用文档在性能方面的比较

19
我读到嵌入式(embedding)是从性能角度考虑更好的选择:“如果性能是一个问题,就使用嵌入式(embed)。”(http://www.mongodb.org/display/DOCS/Schema+Design),并且大多数指南都建议应该使用嵌入式来包含数据。
然而,我不确定这是否正确。假设我们有两个对象:Blog和Post。Blog 包含多篇 Post。
现在将所有的 Post 都嵌入到 Blog 中会有以下问题:
  1. 分页。由于无法过滤嵌入的对象,我们将始终获取所有 Post,并需要在应用程序中进行筛选。
  2. 过滤。与之前相同,当搜索 Post 内的单词时,将无法从 MongoDB 中过滤嵌入的集合。
  3. 插入。我认为向集合中插入数据比向嵌入的对象中插入数据快。这是正确的吗?有没有相关的资料可以查阅?
  4. 更新。与之前相同,对于内联更新较小文档(Post)中的字段,可能比对于 Blog 的大文档中嵌入 Post 进行内联更新更快。这是正确的吗?
考虑到以上问题,我会选择将 Post 放在一个单独的集合中,并引用 Blog。这个结论正确吗?
(注意:请不要考虑文档大小限制,假设每个 Blog 最多有1000篇 Post)
3个回答

15

1.使用$slice操作符可以实现分页:

db.blogs.find({}, {posts:{$slice: [10, 10]}}) // skip 10, limit 10

2. 还可以进行过滤:

db.blogs.find({"posts.title":"Mongodb!"}, {posts:{$slice: 1}}) //take one post

3,4. 我猜你在谈论微小的性能差异。这不是什么高深的技术,只是一个最多有1000篇文章的博客。

你说:

Is this the correct conclusion?

不要使用分离的文档,如果你关心性能的话(一般来说,如果系统很小,你可以使用分离的文档)。

我对 3,4 进行了小型性能测试,这是测试结果:

-----------------------------------------------------------------
| Count/Time |  Inserting posts   | Adding to nested collection |
-------------|--------------------------------------------------               
|   1        |   1 ms             |  28 ms                      |
|   1000     |   81 ms            |  590 ms                     |
|   10000    |   759 ms           |  2723 ms                    |
 ---------------------------------------------------------------

你确定 #2 返回的是标题匹配一个帖子的博客吗?我认为它会返回包含“Mongodb!”标题的帖子的博客。然后,切片将只过滤第一篇文章。因此,你会得到错误的帖子。 - mbdev
请参见:https://dev59.com/43I95IYBdhLWcg3w1BdN - mbdev
@mbdev:#2只是一个虚假的查询。我只是向您展示它如何完成。 - Andrew Orsich
@Andrew 你还在吗?我想在更正之后接受这个答案。 - mbdev
@mbdev:好的,我稍后会做并通知您。谢谢。 - Andrew Orsich
显示剩余4条评论

3
对于第三点和第四点,如果您正在向嵌套文档中插入内容,则基本上属于更新操作。
这可能会影响性能,因为通常情况下插入操作是将数据追加到末尾,工作速度快。而更新操作就要复杂得多。
如果您的更新操作不改变文档的大小(也就是说只是更改了键值对中的值,新值占用与旧值相同的空间),那么一切都还好,但是当您开始修改文档并添加新的数据时,问题就出现了。
问题在于,虽然MongoDB为每个文档分配了比其所需更多的空间,但可能仍然不足。如果您插入了一个1K大小的文档,MongoDB可能会为文档分配1.5K的空间,以确保对文档进行微小更改时有足够的空间增长。如果使用的空间超过了分配的空间,MongoDB必须获取整个文档并在数据末尾重新编写它。
从提取和重新编写数据中显然存在性能影响,而此类操作频率越高,影响就越明显。更糟糕的是,当发生这种情况时,您最终会留下无用空间的空洞或口袋,这些空洞会复制到内存中,这意味着您可能会使用2GB的RAM来存储数据集,而实际上数据本身只占用1.5GB,因为有0.5GB的空洞。通过插入操作而不是更新操作可以避免出现这种分段情况,也可以通过进行数据库修复来解决它。在下一个版本的MongoDB中将提供在线压缩功能。

你认为这些数字会比安德鲁的更糟糕吗? - mbdev
很难说 - 这完全取决于您的数据结构和文档及嵌入式文档的大小。一旦您尝试插入一个大于免费空间限制的文档,您会看到写入性能下降。我认为这将很难通过小型测试和相对较小的数据集来证明。 - Bryan Migliorisi

1
  1. 您可以使用'$slice'在嵌入元素上进行分页。
  2. 您可以使用"field1.field2": /aRegex/进行搜索,其中aRegex是您要搜索的单词。但要注意性能。

关于第3和第4点,我没有证据数据。

顺便说一下,使用两个集合可能更容易编写/使用/管理。您可以在每个“博客”文档中注册blogId,并在所有查询中添加"blogId":"1234ABCD"


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接