MongoDB-使用$ elemMatch在数组中搜索比没有索引慢

祖父

我有一个具有以下结构的500k文档的集合:

{
    "_id" : ObjectId("5f2d30b0c7cc16c0da84a57d"),
    "RecipientId" : "6a28d20f-4741-4c14-a055-2eb2593dcf13",
    
    ...
    
    "Actions" : [ 
        {
            "CampaignId" : "7fa216da-db22-44a9-9ea3-c987c4152ba1",
            "ActionDatetime" : ISODate("1998-01-13T00:00:00.000Z"),
            "ActionDescription" : "OPEN"
        }, 
        ...
    ]
}

我需要计算顶级文档,这些顶级文档的“操作”数组中的子文档满足特定条件,为此,我创建了以下多键索引(仅以“ ActionDatetime”字段为例):

db.getCollection("recipients").createIndex( { "Actions.ActionDatetime": 1 } )

问题是,当我使用$ elemMatch编写查询时,该操作要比完全不使用Multikey索引时慢得多:

db.getCollection("recipients").count({
  "Actions":
    { $elemMatch:{ ActionDatetime: {$gt: new Date("1950-08-04")} }}}
)

此查询的统计信息:

{
    "executionSuccess" : true,
    "nReturned" : 0,
    "executionTimeMillis" : 13093,
    "totalKeysExamined" : 8706602,
    "totalDocsExamined" : 500000,
    "executionStages" : {
        "stage" : "COUNT",
        "nReturned" : 0,
        "executionTimeMillisEstimate" : 1050,
        "works" : 8706603,
        "advanced" : 0,
        "needTime" : 8706602,
        "needYield" : 0,
        "saveState" : 68020,
        "restoreState" : 68020,
        "isEOF" : 1,
        "nCounted" : 500000,
        "nSkipped" : 0,
        "inputStage" : {
            "stage" : "FETCH",
            "filter" : {
                "Actions" : {
                    "$elemMatch" : {
                        "ActionDatetime" : {
                            "$gt" : ISODate("1950-08-04T00:00:00.000Z")
                        }
                    }
                }
            },
            "nReturned" : 500000,
            "executionTimeMillisEstimate" : 1040,
            "works" : 8706603,
            "advanced" : 500000,
            "needTime" : 8206602,
            "needYield" : 0,
            "saveState" : 68020,
            "restoreState" : 68020,
            "isEOF" : 1,
            "docsExamined" : 500000,
            "alreadyHasObj" : 0,
            "inputStage" : {
                "stage" : "IXSCAN",
                "nReturned" : 500000,
                "executionTimeMillisEstimate" : 266,
                "works" : 8706603,
                "advanced" : 500000,
                "needTime" : 8206602,
                "needYield" : 0,
                "saveState" : 68020,
                "restoreState" : 68020,
                "isEOF" : 1,
                "keyPattern" : {
                    "Actions.ActionDatetime" : 1.0
                },
                "indexName" : "Actions.ActionDatetime_1",
                "isMultiKey" : true,
                "multiKeyPaths" : {
                    "Actions.ActionDatetime" : [ 
                        "Actions"
                    ]
                },
                "isUnique" : false,
                "isSparse" : false,
                "isPartial" : false,
                "indexVersion" : 2,
                "direction" : "forward",
                "indexBounds" : {
                    "Actions.ActionDatetime" : [ 
                        "(new Date(-612576000000), new Date(9223372036854775807)]"
                    ]
                },
                "keysExamined" : 8706602,
                "seeks" : 1,
                "dupsTested" : 8706602,
                "dupsDropped" : 8206602
            }
        }
    }
}

该查询执行需要14秒,而如果删除索引,则COLLSCAN需要1秒。

我知道不使用$ elemMatch并直接通过“ Actions.ActionDatetime”进行过滤会获得更好的性能,但实际上,我需要按数组内的多个字段进行过滤,因此$ elemMatch成为强制性的。

我怀疑是FETCH阶段正在降低性能,但是我注意到当我直接使用“ Actions.ActionDatetime”时,MongoDB能够使用COUNT_SCAN而不是fetch,但是性能仍然比COLLSCAN(4秒)。

我想知道是否存在一种更好的索引策略来索引数组内具有高基数的子文档,或者我目前的方法是否缺少某些内容。随着数量的增长,对这些信息建立索引将是必要的,并且我不想依靠COLLSCAN。

这里的问题是双重的:

  • 每个文档都与您的查询相匹配
    考虑类比索引就是库中的目录。如果您想查找一本书,则可以在目录中查找它,这直接使您可以直达拥有该书架的书架,这比从第一个书架开始搜索书本的速度要快得多(除非它实际上位于书架上)第一层)。但是,如果要在图书馆中获取所有书籍,那么开始将它们从书架上拿下来要比检查每本书的目录然后再​​拿起来要快得多。
    尽管这种类比还远非完美,但它确实表明,当考虑要考虑大量文档时,可以预期集合扫描比索引查找要有效得多。

  • 多键索引在每个文档中都有多个条目
    。mongod在数组上建立索引时,它将为每个谨慎元素在索引中创建一个单独的条目。当您匹配数组元素中的值时,索引可以使您快速找到匹配的文档,但是由于此后期望单个文档在索引中具有多个条目,因此需要进行重复数据删除。

这些进一步加剧了$elemMatch由于索引包含单独索引字段的值,因此无法确定不同字段的值是否出现在索引的同一数组元素中,因此必须加载每个文档来进行检查。

本质上,当将elemMatch与索引一起使用并且对每个文档进行匹配的查询时,mongod节点将检查索引以识别匹配值,对列表进行重复数据删除,然后加载每个文档(可能按索引中遇到的顺序)以查看是否存在单数组值满足elemMatch。

当与非索引集合扫描执行比较时,mongod必须按磁盘上遇到的顺序加载每个文档,并检查单个数组元素是否满足elemMatch,显然,如果索引查询的大小较大,则索引查询的性能会更差符合查询条件的文档百分比。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

MongoDB:在数字或字符串数组上使用$ elemMatch

使用elemMatch从MongoDB中的数组中获取数据

如果我在索引上搜索,没有选项 `i` 的 mongodb $regex 是否仍然使用索引?

在数组中搜索关键字,仅使用NodeJS在MongoDB中获得匹配的元素

MongoDB聚合对嵌套数组使用$ elemMatch

使用Java在MongoDB中的嵌入式文档中索引和搜索“数组”

有没有办法在 MongoDB 中使用聚合管道搜索嵌入式数组?

使用AND OR在mongodb中搜索查询

使用MongoDb创建索引

未使用MongoDB索引

mongodb使用什么索引?

在MongoDB中,如何在数组中使用“ $ expr”?

使用存储在数组中的_id从golang查询mongodb

MongoDB:使用多个条件在数组中查找值

如何使用mongodb在数组中投影字段

如何查询-使用Mongodb在数组键值Laravel中的位置

如何使用Mongoose / MongoDB在数组中定义对象

在 mongoDB 中使用 $elemMatch 查询 $in

mongodb 在数组 PHP 中查询搜索

在数组元素上使用$ and的MongoDB findOne()

在数组 mongodb 中使用 concat 的问题

MongoDB全文搜索不使用索引

在mongodb中使用索引搜索值

有没有办法在没有循环的情况下使用 bash 在数组中搜索相同性?

使用数组查找 Mongodb

如何使用$ set和$ elemMatch之类的东西更新MongoDB文档数组中的单个子文档?

如何使用mongoDB按数组中的多个值进行搜索?

在MongoDB中搜索嵌入式数组(使用Java)

使用 MongoDB 慢 .findByIdAndUpdate().lean()