TLDR;将两种不同类型的文档放入同一个集合以保存数据库的往返是否有缺点?
所以我有带孩子的文件,以及引用孩子的父母的钥匙列表,几乎每当我们想要父母时,我们也希望孩子们出现。这种天真的方法是获取父项,然后使用$ IN的子项列表获取子项(在SQL中,我们将使用连接)。但是,这意味着要进行2次往返,以便进行相当频繁的操作。我们有一些选项来改进这一点,特别是因为我们可以在父键的同时检索子键:
将孩子放在父文档中
虽然这会影响mongo的力量,但我们也希望保持这些数据正常化
线程中的管道数据库请求
一旦我们考虑连接池,这可能会也可能不会提高性能。它还意味着在python应用程序中处理线程,这并不可怕,但并不是很好。
将父/子文档保留在同一个集合中(未嵌入)
这样我们可以同时对所有密钥进行一次查询;这确实意味着包装器中的一些概念性开销用于访问数据库,并强制所有索引都是稀疏的,但在其他方面似乎很简单。
我们可以介绍所有这些选项,但它确实感觉有人在那里应该已经有这方面的经验,尽管没有在网上找到任何东西。那么,我的分析中是否缺少某些内容?
答案 0 :(得分:1)
我将分别解决这三点。你应该知道,这完全取决于什么是最好的情况。没有“理论上正确”的答案,因为它取决于您的数据存储/访问模式。
关于如何存储数据始终是一个相当复杂的决定。我认为主要规则应该是“我如何查询我的数据?”,而不是“我们希望所有数据都规范化”。数据规范化是您为关系数据库而不是MongoDB所做的事情。如果您几乎总是使用父级查询子级,并且您没有未绑定的子级列表,那么您应该如何存储它们。请注意,MongoDB中的文档限制为16MB(比您想象的要多得多)。
避免穿线。从两个不同的集合中按顺序运行两个查询会更好。不太复杂是件好事!
这样可行,但这是一种相当丑陋的方式。但话说回来,丑陋并不总是一件坏事,如果它让事情变得更快。我当然不太了解您的父母和子女文件有多么不同,因此很难说这是否是一个好的解决方案。一个稀疏索引,我假设您将根据它是父项还是子项在特定字段上执行,这是一个好主意。但也许你也可以逃脱一个指数。在您展示建议的模式后,我很乐意更新您的答案。
我建议你做一些基准测试,但忘记选项2。
答案 1 :(得分:0)
如果层次结构中有更多级别(对于一个级别也可以正常工作),那么拥有一个祖先数组是一个不错的选择。这是一种不是我发明的技巧,当然它在mongodb开发者课程中有解释:https://education.10gen.com/courses(这只是一个链接,你可以找到我无法直接链接到教程的诅咒)。在这种情况下,您有一个id数组,其中至少包含节点本身和所有祖先ID。如果查询子树,则只需在祖先数组中搜索根。
文件包含:
{ancestors: [2,7,6]}
您在节点6下查询子树:
find({ancestors:6})
这很不错,但在现实世界的例子中,您通常不会查询ID。所以你将有两个查询,一个有意义的东西,一个ids属于那个。
但是,如果您在第一步查询ID,此解决方案可以解决您的问题。
我认为由于mongodb很多东西取决于亲子关系的结构。如果只有一个级别的谱系,没有特别的理由分别存储数据,在这种情况下,丑陋的方式是一个很好的选择,如果你可能有一个复杂的查询(和索引),使用非规范化。
(你真的没有机会查询父项的某些功能并在同一查询中检索子项而不进行非规范化/嵌入)但只有当你的连接数很少时才会嵌入,并且只有当你没有复制大量的连接时才进行非规范化数据。如果将子ID存储在数组中并对其进行索引,那么使用这两个查询的性能不会那么糟糕,与保持清理非规范化数据或使用嵌入处理16MB限制相比,这不是一个非常糟糕的主意。