Mongo $or 查询范围是在内存中排序吗?

Posted

技术标签:

【中文标题】Mongo $or 查询范围是在内存中排序吗?【英文标题】:Mongo $or query with ranges is doing an in-memory sort? 【发布时间】:2020-10-01 16:06:09 【问题描述】:

我遇到了一种特殊情况,其中一个查询似乎是在进行内存排序。查询 1 是执行内存排序的查询,而查询 2 正确执行合并排序。

查询有几个部分,所以我想知道哪个部分导致查询排序在内存中完成?

我确实有一个解决方法,但我想知道这背后的原因。它们都有 2 个输入级,所以我不确定是什么原因。

架构:

schema = 
    date: Date, // date that can change
    createTime: Date, // create time of document
    value: Number

索引:

schema.index(value: 1, createTime: -1, date: 1);

查询 1:我在顶层有 $or 以避免使用不正确的索引:MongoDB query to slow when using $or operator

db.getCollection('dates').find(
    $or: [
        value: $in: [1, 2], date: null,
        value: $in: [1, 2], date: $gt: ISODate("2020-06-16T23:59:59.999Z")
   ]
).sort(createTime:-1).explain()

查询 1 计划:如您所见,它在内存中进行排序。我不确定发生这种情况的确切原因。


    "stage" : "SUBPLAN",
    "inputStage" : 
        "stage" : "FETCH",
        "inputStage" : 
            "stage" : "SORT",
            "sortPattern" : 
                "createTime" : -1.0
            ,
            "inputStage" : 
                "stage" : "SORT_KEY_GENERATOR",
                "inputStage" : 
                    "stage" : "OR",
                    "inputStages" : [ 
                        
                            "stage" : "FETCH",
                            "filter" : 
                                "date" : 
                                    "$eq" : null
                                
                            ,
                            "inputStage" : 
                                "stage" : "IXSCAN",
                                "keyPattern" : 
                                    "value" : 1,
                                    "createTime" : -1,
                                    "date" : 1
                                ,
                                "indexName" : "value_1_createTime_-1_date_1",
                                "isMultiKey" : false,
                                "multiKeyPaths" : 
                                    "value" : [],
                                    "createTime" : [],
                                    "date" : []
                                ,
                                "isUnique" : false,
                                "isSparse" : false,
                                "isPartial" : false,
                                "indexVersion" : 2,
                                "direction" : "forward",
                                "indexBounds" : 
                                    "value" : [ 
                                        "[1.0, 1.0]", 
                                        "[2.0, 2.0]"
                                    ],
                                    "createTime" : [ 
                                        "[MaxKey, MinKey]"
                                    ],
                                    "date" : [ 
                                        "[undefined, undefined]", 
                                        "[null, null]"
                                    ]
                                
                            
                        , 
                        
                            "stage" : "IXSCAN",
                            "keyPattern" : 
                                "value" : 1,
                                "createTime" : -1,
                                "date" : 1
                            ,
                            "indexName" : "value_1_createTime_-1_date_1",
                            "isMultiKey" : false,
                            "multiKeyPaths" : 
                                "value" : [],
                                "createTime" : [],
                                "date" : []
                            ,
                            "isUnique" : false,
                            "isSparse" : false,
                            "isPartial" : false,
                            "indexVersion" : 2,
                            "direction" : "forward",
                            "indexBounds" : 
                                "value" : [ 
                                    "[1.0, 1.0]", 
                                    "[2.0, 2.0]"
                                ],
                                "createTime" : [ 
                                    "[MaxKey, MinKey]"
                                ],
                                "date" : [ 
                                    "(new Date(1592351999999), new Date(9223372036854775807)]"
                                ]
                            
                        
                    ]
                
            
        
    

查询 2:

db.getCollection('dates').find(
    value: $in: [1, 2],
    date: $not: $lte: ISODate("2020-06-16T23:59:59.999Z")
).sort(createTime:-1).explain()

查询 2 计划:我使用的解决方法查询,它成功进行了合并排序。


    "stage" : "FETCH",
    "inputStage" : 
        "stage" : "SORT_MERGE",
        "sortPattern" : 
            "createTime" : -1.0
        ,
        "inputStages" : [ 
            
                "stage" : "IXSCAN",
                "keyPattern" : 
                    "value" : 1,
                    "createTime" : -1,
                    "date" : 1
                ,
                "indexName" : "value_1_createTime_-1_date_1",
                "isMultiKey" : false,
                "multiKeyPaths" : 
                    "value" : [],
                    "createTime" : [],
                    "date" : []
                ,
                "isUnique" : false,
                "isSparse" : false,
                "isPartial" : false,
                "indexVersion" : 2,
                "direction" : "forward",
                "indexBounds" : 
                    "value" : [ 
                        "[1.0, 1.0]"
                    ],
                    "createTime" : [ 
                        "[MaxKey, MinKey]"
                    ],
                    "date" : [ 
                        "[MinKey, true]", 
                        "(new Date(1592351999999), MaxKey]"
                    ]
                
            , 
            
                "stage" : "IXSCAN",
                "keyPattern" : 
                    "value" : 1,
                    "createTime" : -1,
                    "date" : 1
                ,
                "indexName" : "value_1_createTime_-1_date_1",
                "isMultiKey" : false,
                "multiKeyPaths" : 
                    "value" : [],
                    "createTime" : [],
                    "date" : []
                ,
                "isUnique" : false,
                "isSparse" : false,
                "isPartial" : false,
                "indexVersion" : 2,
                "direction" : "forward",
                "indexBounds" : 
                    "value" : [ 
                        "[2.0, 2.0]"
                    ],
                    "createTime" : [ 
                        "[MaxKey, MinKey]"
                    ],
                    "date" : [ 
                        "[MinKey, true]", 
                        "(new Date(1592351999999), MaxKey]"
                    ]
                
            
        ]
    

【问题讨论】:

【参考方案1】:

$or 的每个分支都可以使用一个索引,但是您仍然有两个结果集,如果您在顶部应用排序,则数据库必须在内存中对结果进行排序。对 $or 运算符进行排序会产生内存排序似乎是合理的。

【讨论】:

但是为什么不能像第二个查询那样进行合并排序呢?它在第二个查询中也有 2 个阶段。是不是各个阶段本身没有排序,所以不能合并? 什么是归并排序? 我认为归并排序不正确。这是一个排序合并,所以合并2个排序的数组。 我不明白你在说什么。如果您暗示 MongoDB 的行为与其文档(或其他已发布的声明)相矛盾,请参考您认为相矛盾的文档。 抱歉不清楚。我指的是查询 2 计划中的 SORT_MERGE 阶段。我在归并排序中命名,但我认为这意味着合并 2 个排序数组而不是使用归并排序排序。所以我只是对命名感到困惑。

以上是关于Mongo $or 查询范围是在内存中排序吗?的主要内容,如果未能解决你的问题,请参考以下文章

Mongodb:从 mongo shell 中的 ObjectId 执行日期范围查询

OperationFailed Sort operation used more than the maximum 33554432 bytes of RAM. Add an index, or sp

如何使用 OR 而不是 AND 链接范围查询?

MongoDB - 时间序列子文档的范围查询

Atitit Mysql查询优化器 存取类型 范围存取类型 索引存取类型 AND or的分析

solr 中的布尔子句异常过多