【问题标题】:Unacceptably slow MongoDB $lte + $gte query with index带索引的 MongoDB $lte + $gte 查询速度慢得令人无法接受
【发布时间】:2017-10-10 19:06:04
【问题描述】:

我正在使用 64 GB RAM 的笔记本电脑上运行 Community MongoDB 3.4.9。我有一个包含 12+ 百万个文档的集合。每个文档至少有 fromto 类型为 Int64 的字段。 from-to 是唯一的范围。没有具有重叠范围的文档。集合上有一个索引如下:

{ 
    "v" : NumberInt(1), 
    "unique" : true, 
    "key" : {
        "from" : NumberInt(1), 
        "to" : NumberInt(1)
    }, 
    "name" : "range", 
    "ns" : "db.location", 
    "background" : true
}

服务器/数据库空闲。没有客户。我一遍又一遍地运行下面的查询,我得到了大约 21 秒的恒定执行时间。

db.location.find({from:{$lte:NumberLong(3682093364)},to:{$gte:NumberLong(3682093364)}}).limit(1)

and 条件的反转不会对执行时间产生影响。 explain 命令显示以下内容。

{ 
    "queryPlanner" : {
        "plannerVersion" : 1.0, 
        "namespace" : "db.location", 
        "indexFilterSet" : false, 
        "parsedQuery" : {
            "$and" : [
                {
                    "from" : {
                        "$lte" : NumberLong(3682093364)
                    }
                }, 
                {
                    "to" : {
                        "$gte" : NumberLong(3682093364)
                    }
                }
            ]
        }, 
        "winningPlan" : {
            "stage" : "LIMIT", 
            "limitAmount" : 1.0, 
            "inputStage" : {
                "stage" : "FETCH", 
                "inputStage" : {
                    "stage" : "IXSCAN", 
                    "keyPattern" : {
                        "from" : 1.0, 
                        "to" : 1.0
                    }, 
                    "indexName" : "range", 
                    "isMultiKey" : false, 
                    "multiKeyPaths" : {
                        "from" : [

                        ], 
                        "to" : [

                        ]
                    }, 
                    "isUnique" : true, 
                    "isSparse" : false, 
                    "isPartial" : false, 
                    "indexVersion" : 1.0, 
                    "direction" : "forward", 
                    "indexBounds" : {
                        "from" : [
                            "[-inf.0, 3682093364]"
                        ], 
                        "to" : [
                            "[3682093364, inf.0]"
                        ]
                    }
                }
            }
        }, 
        "rejectedPlans" : [

        ]
    }, 
    "executionStats" : {
        "executionSuccess" : true, 
        "nReturned" : 1.0, 
        "executionTimeMillis" : 21526.0, 
        "totalKeysExamined" : 12284007.0, 
        "totalDocsExamined" : 1.0, 
        "executionStages" : {
            "stage" : "LIMIT", 
            "nReturned" : 1.0, 
            "executionTimeMillisEstimate" : 20945.0, 
            "works" : 12284008.0, 
            "advanced" : 1.0, 
            "needTime" : 12284006.0, 
            "needYield" : 0.0, 
            "saveState" : 96299.0, 
            "restoreState" : 96299.0, 
            "isEOF" : 1.0, 
            "invalidates" : 0.0, 
            "limitAmount" : 1.0, 
            "inputStage" : {
                "stage" : "FETCH", 
                "nReturned" : 1.0, 
                "executionTimeMillisEstimate" : 20714.0, 
                "works" : 12284007.0, 
                "advanced" : 1.0, 
                "needTime" : 12284006.0, 
                "needYield" : 0.0, 
                "saveState" : 96299.0, 
                "restoreState" : 96299.0, 
                "isEOF" : 0.0, 
                "invalidates" : 0.0, 
                "docsExamined" : 1.0, 
                "alreadyHasObj" : 0.0, 
                "inputStage" : {
                    "stage" : "IXSCAN", 
                    "nReturned" : 1.0, 
                    "executionTimeMillisEstimate" : 20357.0, 
                    "works" : 12284007.0, 
                    "advanced" : 1.0, 
                    "needTime" : 12284006.0, 
                    "needYield" : 0.0, 
                    "saveState" : 96299.0, 
                    "restoreState" : 96299.0, 
                    "isEOF" : 0.0, 
                    "invalidates" : 0.0, 
                    "keyPattern" : {
                        "from" : 1.0, 
                        "to" : 1.0
                    }, 
                    "indexName" : "range", 
                    "isMultiKey" : false, 
                    "multiKeyPaths" : {
                        "from" : [

                        ], 
                        "to" : [

                        ]
                    }, 
                    "isUnique" : true, 
                    "isSparse" : false, 
                    "isPartial" : false, 
                    "indexVersion" : 1.0, 
                    "direction" : "forward", 
                    "indexBounds" : {
                        "from" : [
                            "[-inf.0, 3682093364]"
                        ], 
                        "to" : [
                            "[3682093364, inf.0]"
                        ]
                    }, 
                    "keysExamined" : 12284007.0, 
                    "seeks" : 12284007.0, 
                    "dupsTested" : 0.0, 
                    "dupsDropped" : 0.0, 
                    "seenInvalidated" : 0.0
                }
            }
        }, 
        "allPlansExecution" : [

        ]
    }, 
    "serverInfo" : {
        "host" : "LAPTOP-Q96TVSN8", 
        "port" : 27017.0, 
        "version" : "3.4.9", 
        "gitVersion" : "876ebee8c7dd0e2d992f36a848ff4dc50ee6603e"
    }, 
    "ok" : 1.0
}

提供hint 并没有什么不同。 explain 似乎表明正确的(也是唯一的)索引已被使用,但大部分执行时间(20 秒)都花在了 IXSCAN 上。 MongoDB 日志显示扫描了许多索引项,但只有一个文档被触摸并返回。考虑到数据库上的并发操作为零,它还显示了大量的锁和收益。底层引擎是 SSD 磁盘上的wiredTiger。 MongoDB RAM 使用量为 7 GB。

2017-10-10T10:06:14.456+0200 I COMMAND  [conn33] command db.location appName: "MongoDB Shell" command: explain { explain: { find: "location", filter: { from: { $lte: 3682093364 }, to: { $gte: 3682093364 } }, limit: 1.0, singleBatch: false }, verbosity: "allPlansExecution" } numYields:96299 reslen:1944 locks:{ Global: { acquireCount: { r: 192600 } }, Database: { acquireCount: { r: 96300 } }, Collection: { acquireCount: { r: 96300 } } } protocol:op_command 21526ms

考虑到我的范围从不重叠,是否有更好的方法来构建文档以便查找更快?有什么明显的我做错了吗?

更新:

当我删除索引时,使用COLLSCAN 并在一致的 8-9 秒内找到文档。

【问题讨论】:

    标签: mongodb mongodb-query


    【解决方案1】:

    我讨厌回答自己的问题,但我很高兴找到解决方案。

    尽管创建这样一个复合索引是有意义的,但考虑到非重叠范围的细节,结果表明搜索范围太宽泛了。输入的数字越大,找到结果的时间就越长,因为找到满足from <= number的索引条目越来越多,搜索范围内的最后一个结果实际上是我们要查找的结果(索引从左到对)。

    解决方案是将索引修改为{ from: -1 }{ to: 1 }。在这种情况下,实际上不需要复合索引,因为范围不重叠,并且索引找到的第一个文档就是要返回的文档。现在正如预期的那样快如闪电。

    你生活和学习......

    【讨论】:

    • 这个信息对我很有用,我刚刚遇到了类似的问题。
    • 这很有帮助。但为了我的清楚,-1 索引应该用于 $lte 和 +1 用于 $gte?
    • 那是很久以前的事了,但从目前写的所有内容来看,我会说 - 是的!
    • 确实很有帮助。
    猜你喜欢
    • 1970-01-01
    • 2016-11-17
    • 2016-12-23
    • 2013-06-07
    • 1970-01-01
    • 2015-12-27
    • 2016-08-28
    • 1970-01-01
    • 2021-04-17
    相关资源
    最近更新 更多