当前位置: 首页 > 知识库问答 >
问题:

在具有索引的小集合上,$查找/$排序性能较差

胡星汉
2023-03-14

我已经在SO上浏览了近10篇类似的帖子,但我仍然对我得到的结果感到困惑:在42K文档集合和19条记录集合之间的单个$查找聚合上,在外部字段上进行排序需要5秒。也就是798K的总向量积。

不幸的是,在这里,非规范化不是一个很好的选择,因为“to”集合中的文档被大量共享,并且在进行更改时需要在整个数据库中进行大量更新。

话虽如此,我似乎不明白为什么接下来要花这么长时间。我觉得我一定做错了什么。

上下文:

>

  • 一个4 vCPU、16 GB RAM VM作为单个节点副本集运行Debian 10/MongoDB 4.4,仅此而已。完全更新。NET MongoDB驱动程序(我刚刚更新并重新测试)

    聚合中有一个查找,“from”集合有42K个文档,“to”集合有19个文档。

    所有聚合、索引和集合都使用默认排序规则。

    “to”集合中的外部字段具有索引。是的,只是为了那19张唱片,以防万一会有所不同。

    其中一篇关于慢速$查找性能的帖子提到,如果$查找阶段的嵌套管道中没有使用$eq,它将不会使用索引。因此,我确保聚合管道使用$eq操作符。

    管道是这样的:

    [{ "$lookup" : 
        { "from" : "4", 
        "let" : { "key" : "$1" }, 
        "pipeline" : 
            [{ "$match" : 
                { "$expr" : 
                    { "$eq" : ["$id", { "$substrCP" : ["$$key", 0, { "$indexOfCP" : ["$$key", "_"] }] }] } } }, 
            { "$unwind" : { "path" : "$RF2" } }, 
            { "$match" : { "$expr" : { "$eq" : ["$RF2.id", "$$key"] } } }, 
            { "$replaceRoot" : { "newRoot" : "$RF2" } }], 
        "as" : "L1" } }, 
    { "$sort" : { "L1.5" : 1 } },
    { "$project" : { "L1" : 0 } }, 
    { "$limit" : 100 }]
    

    去掉嵌套的$unwind/$match/$replaceRoot组合会占用大约30%的运行时间,将其缩短到3.5秒,但是,这些阶段对于链接/查找到适当的子文档是必要的。在0.5秒内对“from”集合进行排序,无需查找。

    我做错了什么?提前感谢!

    编辑:

    我刚刚用一组更大的记录测试了同样的事情(“from”集合中的38K记录,“to”集合中的26K记录,一对一的关系)。完成排序花了7分钟以上。我在Compass中检查并看到“id”上的索引实际上正在使用(在7分钟内不断刷新并看到它上升,我目前是数据库的唯一用户)。

    这是一条比第一条更简单的管道:

    [{ "$lookup" : 
        { "from" : "1007", 
        "let" : { "key" : "$13" }, 
        "pipeline" : 
            [{ "$match" : 
                { "$expr" : { "$eq" : ["$id", "$$key"] } } }], 
        "as" : "L1" } }, 
    { "$sort" : { "L1.1" : -1 } }, 
    { "$project" : { "L1" : 0 } }, 
    { "$limit" : 100 }]
    

    考虑到上述信息,7分钟听起来合理吗?

    编辑2:

    shell代码创建两个40k记录集合(prod和prod2),两个字段(name: string, uid: integer):

    var randomName = function() { 
        return (Math.random()+1).toString(36).substring(2); 
    }
    
    for (var i = 1; i <= 40000; ++i) { 
        db.test.insert({ 
            name: randomName(), 
            uid: i }); 
    }
    

    我在prod2的“uid”字段上创建了一个索引,将Compass的示例文档限制增加到50k,然后只进行了以下查找,这花费了整整两分钟的时间进行计算:

    { from: 'prod2', 
    localField: 'uid', 
    foreignField: 'uid', 
    as: 'test' }
    

    编辑3:

    我刚刚还直接从shell运行了聚合管道,并在几秒钟内而不是两分钟内获得了结果:

    db.test1.aggregate([{ $lookup:
    { from: 'test2', 
      localField: 'uid', 
      foreignField: 'uid', 
      as: 'test' } }]).toArray()
    

    是什么导致炮弹和指南针以及。网络驱动程序?

  • 共有1个答案

    太叔景曜
    2023-03-14

    对于任何无意中读到这篇文章的人来说,以下内容对我很有用:使用$lookup操作符的localField/foreignField版本。

    在Compass中监视索引时,let/管道和localField/外国字段版本都达到了适当的索引,但在使用let/管道版本时速度慢了几个数量级。

    我重新构造了查询构建逻辑,只使用localField/foreignField,这带来了很大的不同。

     类似资料:
    • 问题内容: 我有一些复杂的对象,例如猫,它具有许多属性,例如年龄,喜爱的猫食等等。 Java集中存储了一堆猫,我需要查找所有3岁的猫,或者最喜欢猫粮的Whiskas。当然,我可以编写一个自定义方法来查找那些具有特定属性的Cat,但是这样做会麻烦许多属性。有一些通用的方法吗? 问题答案: 您可以编写一个采用接口实例的方法,该实例定义了一个方法,该方法可以通过所需的任何属性检查来实现。 更好的是,使其

    • 我有一个排序的值数组和一个值,如下所示: 我可以找到在保持排序顺序的同时将插入到中的值的索引: 代码很好而且紧凑,但我有一种直觉,在幕后这是非常低效的:因为不知道数组已排序,所以它必须检查所有值。 有没有更好的方法找到这个指数值? 注意:我对实际合并到不感兴趣,我只想要索引值。

    • 我有一个包含元素列表的实体,现在我想搜索这些元素的属性。这个约束应该是“与”连接的。请看这些简单的例子: 现在我想找到某个城市的家长,比如说“BigCity”,孩子们在学校“AwesomeSchool”,他们在六年级一班。我只想通过CriteriaBuilder获得搜索结果。 到目前为止,我得到了: 不幸的是,这里有两个问题:-看起来我无法在列表属性上调用-这将返回所有孩子在“AwesomeSch

    • 我有两个收藏品 员额:

    • 假设我有三个指数:城市、博物馆和景点。 现在我正在查询一个术语的所有索引(),例如“维也纳” 作为结果,我得到: 维也纳:维也纳艺术博物馆 有没有办法优先考虑指数,这样我就可以得到第一个城市,而不是景点,最后是博物馆,就像这样: 维也纳 维也纳的Riesenrad 维也纳:维也纳艺术博物馆 维也纳:维也纳历史博物馆

    • 问题内容: 我需要计算出 仅 在python中所有可能的 大小写 排列的列表,例如,使用ar输入它将返回[‘ar’,’Ar’,’aR’,’AR’] 或arc [‘arc’,’ Arc”,“ ARc”,“ aRc”,“ aRC”,“ ARC”],我知道可能有一些不错的方法,但对于我的一生,我无法弄清楚。 问题答案: