我还没准备好放手,这就是为什么我重新考虑问题并编辑了Q(原文如下)。
我正在使用mongoDB进行周末项目,它需要DB中的一些关系,这就是痛苦的全部:
我有三个系列:
Users
Lists
Texts
用户可以拥有文本和列表 - 列出“包含”文本。文本可以在多个列表中。
我决定使用单独的集合(不是嵌入),因为子文档并不总是出现在父文档的上下文中(例如,所有文本,而不在列表中)。
因此,需要做的是将属于某些列表的文本与这些列表完全相同。可以有无限的列表和文本,但列表相比之下会更少。
与我最初想到的相反,我还可以将引用放在每个文本文档中,而不是列表文档中的所有text-id。它实际上会产生影响,因为我可以通过一个查询来查找列表中的每个片段。甚至可以索引该参考。
var TextSchema = new Schema({
_id: Number,
name: String,
inListID: { type : Array , "default" : [] },
[...]
很少有文本会出现在MANY列表中,因此数组不会真正爆炸。问题仍然存在,是否有可能扩展或实际上是用mongoDB实现它的更好方法?是否有助于限制文本可能的列表数量(可能)?是否有很少的配方:很多关系?
获取对已经完成的项目及其实现方式的参考甚至是很棒的(很少:很多关系)。我不敢相信,只要需要一些关系,每个人都会回避mongo DB。
我将在目前为止看到的两个问题中将其分解: 1)假设一个列表由5个文本组成。如何引用列表中包含的文本?只需打开一个数组并将文本的_ids存储在那里?似乎那些阵列可能会长到月球和背部,减慢应用程序的速度?另一方面,文本需要没有列表,因此嵌入不是一个真正的选择。如果我想获得包含100个文本的列表的所有文本,那该怎么办...听起来像两个查询和一个包含100个字段的数组: - /。这种引用正确方法的方式是这样的吗?
var ListSchema = new Schema({
_id: Number,
name: String,
textids: { type : Array , "default" : [] },
[...]
问题2)我看到这种方法是在删除文本时清理引用。它的引用仍然在包含文本的每个列表中,我不想遍历所有列表来清除那些死引用。或者我会吗?有没有一种聪明的方法来解决这个问题?只是让文本保持引用(它们在哪个列表中)只是移动问题,所以这不是一个选项。
我想我不是第一个遇到这类问题的人,但我也无法找到关于如何做到这一点的明确答案。
我也对这种引用(多对多?)的最佳实践以及特别是可伸缩性/性能的一般想法感兴趣。
答案 0 :(得分:6)
关系通常不是一个大问题,但涉及关系的某些操作可能是。这在很大程度上取决于您尝试解决的问题,并且非常强烈地取决于结果集的基数和键的选择性。
我写过a simple testbed,它会根据典型的长尾分布生成数据。事实证明,MongoDB在关系方面通常比人们所认为的更好。
毕竟,关系数据库只有三个不同之处:
$in
的手动子查询在$in
- 子句中可以扩展到数千项,前提是参考值已编入索引,当然如果您需要执行大型连接,即如果您的查询是真正的关系,并且您需要相应地加入大量数据,那么MongoDB可能不太合适。但是,关系数据库中所需的许多连接都不是真正的关系,它们是必需的,因为您必须将对象拆分为多个表,例如因为它包含列表。
一个真正的'关系查询可以是"找到所有购买了产品的客户,这些产品在6月份的营业额中排名靠前的客户获得了4星级评价"。除非您有一个非常专业的架构,基本上是为了支持这个查询而构建的,否则您很可能需要查找所有订单,按客户ID对它们进行分组,获取顶部的 n 结果,使用这些是使用$in
查询评分并使用其他$in
查找实际客户。尽管如此,如果你可以限制自己到6月的10万客户,这是三次往返和一些快速$in
查询。
只要RAM中的索引支持您的查询并且网络不完全拥塞,那么在典型的云硬件上可能会在10-30ms的范围内。在这个例子中,如果数据太稀疏,事情会变得混乱,即前10k用户几乎没有写过4个星级评论,这会迫使你编写足够智能的程序逻辑来继续迭代第一步,既复杂又复杂。但是,如果这是一个非常重要的情况,那么无论如何都可能有更适合的数据结构。
答案 1 :(得分:4)
将MongoDB与引用一起使用是解决性能问题的途径。什么是不使用的完美例子。这是m:n
种关系,m
和n
可以扩展到数百万。 MongoDB在我们拥有1:n(few)
,1:n(many)
,m(few):n(many)
的地方运行良好。但不是在你有m(many):n(many)
的情况下。这显然会导致2次查询和大量的内务管理。
答案 2 :(得分:1)
我不确定这个问题是否仍然存在,但我有类似的经历。
首先,我想说一下官方mongo documentation:
在以下情况下使用嵌入式数据模型:您具有一对一或一对多模型。
对于模型多对多使用与文档引用的关系。
我认为是答案)但这个答案提供了很多问题,因为:
每个项目 - 即使你在周末项目工作,也是一项巨大的责任。这可能意味着您应该编写许多代码来提供系统的简单行为(例如,您可以看到如何在mongo here中实现事务。)
我不知道外键约束是怎么做的,我没有在mongo文档中看到这个方向的东西,这就是为什么我认为这是一个惊人的挑战(以及项目的风险)。
最后,mongo引用 - 它不是mysql join,并且你没有收到来自父集合的所有数据与来自子集合的数据(如表中的所有字段和mysql中连接表中的所有字段),你将收到只需 REFERENCE 到另一个集合中的另一个文档,您将需要对此引用(取消引用)执行某些操作。 它可以通过回调轻松到达,但仅在您只需要一个列表中的一个文本的情况下,但如果您需要一个列表中的所有文本 - 这很糟糕,但如果您需要多个列表中的所有文本 - 它是变成噩梦...
也许这不是我最好的经历......但我认为你应该考虑一下......
答案 3 :(得分:0)
在MongoDB中使用数组通常不是优选的,通常不会被专家建议。
这是一个我想到的解决方案:
<StatusBarItem Content="{Binding CurrentCodingSpecification, Mode=TwoWay}" HorizontalAlignment="Left" VerticalAlignment="Center" Width="200"/>
的每个文档始终是唯一的。 Users
中的单个文档可以有Lists
和Texts
。因此,Users
和Lists
有一个用户名ID字段,该字段将是Texts
的{{1}}。
_id
始终拥有Users
中的所有者,因此它们会按原样存储。
Lists
的所有者可以是Users
或Texts
,因此您还应该在其中保留一个LIST ID字段,其中Users
为List
}}
现在请注意_id
不能同时拥有用户ID和LIST ID,因此您必须保持一个条件,即两者中只有一个,另一个应该是Lists
以便我们可以很容易地知道谁是Texts
的主要所有者。
答案 4 :(得分:0)
写一个答案,因为我想解释一下我将如何从这里开始。
考虑到这里的答案以及我自己对这个主题的研究,实际上将这些引用(不是真正的关系)存储在一个数组中可能会很好,试图保持它相对较小:我的可能性不到1000个字段情况下。
特别是因为我可以逃脱一个查询(我首先虽然我不能)但到目前为止甚至不需要使用$in
,我相信这种方法会扩展。毕竟这只是一个“周末项目”,所以如果没有,我最终会重写 - 那没关系。
使用这样的文本架构:
var textSchema = new Schema({
_id: {type: Number, required: true, index: { unique: true }},
...
inList: { type : [Number] , "default" : [], index: true }
});
我可以使用此查询简单地获取列表中的所有文本,其中inList
是包含列表中文本_ids
的索引数组。
Text.find({inList: listID}, function(err, text) {
...
});
我仍然需要处理外键约束并编写我自己的“清理”函数,如果删除列表则负责删除引用 - 删除列表中每个文本中的引用。 幸运的是,这种情况很少发生,所以我可以偶尔浏览一下每一个文本。
另一方面,如果删除文本,我不必关心删除列表文档中的引用,因为我只将引用存储在关系的一侧(在文本文档中)。在我看来,这一点非常重要!
@mnemosyn:感谢链接并指出这确实不是一个大型连接或换句话说:只是一个非常简单的关系。关于这些复杂操作需要多长时间的一些数字(ofc.hardware dependet)也是一个很大的帮助 PS:GrüßeausBielefeld。
我在自己的研究中发现最有帮助的was this vid,其中Alvin Richards也谈到了最近几分钟的多对多关系。 17.这就是我想到让关系片面的想法,以节省自己清理死亡参考的工作。
感谢帮助人员