gpt4 book ai didi

MongoDB 2.6索引设置,使用$ or,$ in进行查询,并带有限制和排序

转载 作者:IT老高 更新时间:2023-10-28 13:30:09 27 4
gpt4 key购买 nike

我有一个稍微复杂的查询,这对我的应用程序非常重要。

$cur = $col->find(
array (
'$or' => array(
array('owner' => $my_id),
array('owner' => array('$in' => $friends), 'perm.type' => array('$in' => array('P', 'F'))),
array('owner' => array('$in' => $friends), 'perm.list' => $my_id)
)
)
)->limit(10)->skip(0)->sort(array('ca' => -1));

目的是找到前10个帖子,按其创建时间以降序排列,它们是:

一种)。由我自己制作,或
b)。由我的 friend 制作,公开类型为“P”, friend 为“F”,或者
C)。由我的 friend 制作的,权限列表已专门将我指定为查看者。

变量$ friends是与我成为 friend 的用户ID的数组。
perm.type共有4个值,分别是'P','F','S','C'。
perm.list是有权查看此帖子的用户ID的数组。

上面的查询可以按预期方式过滤出正确的结果。但是我遇到了在它们上创建有效索引的问题。

我为此查询创建的索引是:
$col->ensureIndex(array('owner' => 1, 'ca' => -1));
$col->ensureIndex(array('owner' => 1, 'perm.type' => 1, 'ca' => -1));
$col->ensureIndex(array('owner' => 1, 'perm.list' => 1, 'ca' => -1));

第一个索引是为查询条件的第一部分设计的,第二个索引是为第二个条件设计的,而第三个索引是为第三个条件设计的,它是一个多键索引。

一个典型的帖子看起来像这样:
{
"_id": "...",
"owner": "001",
"perm": {
"type": "P",
"list": []
},
"msg": "Nice dress!",
"ca": 1390459269
}

另一个例子:
{
"_id": "...",
"owner": "007",
"perm": {
"type": "C",
"list": ["001", "005"]
},
"msg": "Nice day!",
"ca": 1390837209
}

我知道MongoDB版本2.6之前存在的局限性,这会阻止在将$ or与sort()结合使用时使用索引。根据 http://jira.mongodb.org/browse/SERVER-1205的问题应该已经在2.6中修复。

可以肯定的是,explain()现在可以显示我的索引的使用,而在2.4版本中则没有。但是,当我运行查询时,它现在比不使用任何索引时要慢得多。说明()表明nscanned比预期的要高。经过一番搜索,我发现了这个问题 https://jira.mongodb.org/browse/SERVER-3310,这似乎可以解释我遇到的问题。但是正如票证所述,此问题应该已经在2.5.5中修复,那么是什么导致我的问题呢?

我试图设置不同的索引,以不同的顺序将它们混合,甚至将它们分开,然后检查新的索引交集功能是否会有所帮助。但是没有一个有效。

有人知道我的问题在这里吗?

编辑
经过更多的测试,观察和思考,我缩小了范围,实际上是在一个导致问题的查询中同时使用了$ in,limit()和sort()。对于每个“$ or”子句,添加顶级“$ or”只会使此问题加倍。我将在下面解释我的逻辑:

我已经将我的索引细化为以下内容:
$col->ensureIndex(array('owner._id' => 1, 'ca' => -1, 'perm.type' => 1));
$col->ensureIndex(array('perm.list' => 1, 'ca' => -1, 'owner._id' => 1))

第一个索引的原因是当我有数百万条记录时,查询应首先从给定的用户ID( friend )集中查找,以缩小选择范围。然后,它按照记录的倒序顺序遍历它,以检查每个记录是否具有正确的权限类型。该索引的问题在于查询优化器不知道要满足limit(10)条件需要扫描多少条记录。它不知道最近的10条记录最终将来自何处,因此它必须从'$ in'子句中指定的每个ID返回最多10条记录,然后对每个'$ or'重复相同的操作。因此,如果我有两个“$ or”子句,每个子句都有一个“$ in”,该子句由100个用户ID组成,则它将必须扫描足够的记录以匹配来自“$ in”子句中每个用户的10条记录。第一个“$ or”,然后是第二个“$ or”的“$ in”中每个用户的10条记录,返回2000条记录(这是解释中返回的n,nscanned会更高取决于需要扫描多少条记录才能找到2000条匹配项,并且从这2000条记录中,所有记录均已按时间顺序排序,因此返回前10位。

因此,如果我按以下顺序构建索引:“'ca'=> -1,'owner._id'=> 1,'perm.type'=> 1”,该怎么办?好吧,我真的不能做到这一点,因为当我有成千上万的用户,拥有数百万条记录时,大多数记录与查看者无关。因此,如果我先从'ca'=> -1开始,它会先扫描许多不相关的记录,然后再命中一个符合条件的记录,即使它发现的每个命中都将直接计入limit(10),并且它将只需要扫描尽可能多的记录即可匹配10个符合条件的记录。但是这种扫描可以是成千上万条记录,甚至更多。最糟糕的是,如果找不到10条记录,则必须遍历整个索引才能找到该记录。

第二个索引是查看为我指定的每条记录,以相反的时间顺序浏览它,并检查它们是否来自我的 friend 。这是非常简单的,这里的问题实际上是通过结合使用,以及上面的'$ in',limit()和sort()一起在一个查询中进行的。

在这一点上,我正在从合并应用程序端的结果中寻找解决方案,但是在应用程序端分解“$ or”很容易,但是我如何在标准数组中分解“$ in”( '所有者'=> array('$ in'=> $ friends),'perm.type'=> array('$ in'=> array('P','F'))))?

最佳答案

我不确定这是否是MongoDB 2.6中的错误,但是您可以看一下有关索引创建的this article

The order of fields in an index should be:

1. First, fields on which you will query for exact values.
2. Second, fields on which you will sort.
3. Finally, fields on which you will query for a range of values.


因此,遵循该建议,您可以尝试使用以下索引:
$col->ensureIndex(array('owner' => 1, 'ca' => -1));
$col->ensureIndex(array('ca' => -1, 'owner' => 1, 'perm.type' => 1));
$col->ensureIndex(array('perm.list' => 1, 'ca' => -1, 'owner' => 1));

编辑:

从您的解释来看,如果您正在测试小型数据集,则由于MongoDB不需要阅读大量文档,因此完整收集非常快。您应该尝试对10000个文档进行测试,以查看实际差异。索引中字段的值应足够不同,以确保查询具有索引选择性(例如,并非所有文档都来自同一所有者)。

关于MongoDB 2.6索引设置,使用$ or,$ in进行查询,并带有限制和排序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23263526/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com