MongoDB 查询执行计划和查询分析

2023-04-15 00:00:00 查询 执行 计划

查询执行计划和查询分析是 MongoDB 中用于优化查询性能的重要工具。查询执行计划可以显示查询使用了哪些索引、如何匹配查询条件等信息,帮助开发者理解查询的执行过程,优化索引结构、查询方式等。查询分析则可以统计查询的耗时、扫描文档数等指标,帮助开发者找出查询性能瓶颈。

以下是一个使用查询执行计划和查询分析的示例:

假设有一个名为“users”的集合,其中保存了许多用户信息。我们希望查找所有用户名为“pidancode.com”的用户,并按照注册时间倒序排列。以下是一个查询语句:

db.users.find({ username: "pidancode.com" }).sort({ signupTime: -1 })

我们可以使用 explain() 方法获取这个查询的执行计划:

db.users.find({ username: "pidancode.com" }).sort({ signupTime: -1 }).explain()

执行结果可能类似于以下内容:

{
    "queryPlanner" : {
        "plannerVersion" : 1,
        "namespace" : "test.users",
        "indexFilterSet" : false,
        "parsedQuery" : {
            "username" : {
                "$eq" : "pidancode.com"
            }
        },
        "queryHash" : "99D57EF8543532FD68713ED8418F7FBA",
        "planCacheKey" : "8459549D1357F36B5E3F5D5B03BB2952",
        "winningPlan" : {
            "stage" : "SORT",
            "sortPattern" : {
                "signupTime" : -1
            },
            "inputStage" : {
                "stage" : "SORT_KEY_GENERATOR",
                "inputStage" : {
                    "stage" : "FETCH",
                    "filter" : {
                        "username" : {
                            "$eq" : "pidancode.com"
                        }
                    },
                    "inputStage" : {
                        "stage" : "IXSCAN",
                        "keyPattern" : {
                            "signupTime" : -1
                        },
                        "indexName" : "signupTime_-1",
                        "isMultiKey" : false,
                        "multiKeyPaths" : {
                            "signupTime" : []
                        },
                        "isUnique" : false,
                        "isSparse" : false,
                        "isPartial" : false,
                        "indexVersion" : 2,
                        "direction" : "backward",
                        "indexBounds" : {
                            "signupTime" : [
                                "[MaxKey, MinKey]"
                            ]
                        }
                    }
                }
            }
        },
        "rejectedPlans" : []
    },
    "executionStats" : {
        "executionSuccess" : true,
        "nReturned" : 100,
        "executionTimeMillis" : 47,
        "totalKeysExamined" : 100,
        "totalDocsExamined" : 100,
        "executionStages" : {
            "stage" : "SORT",
            "nReturned" : 100,
            "executionTimeMillisEstimate" : 10,
            "works" : 101,
            "advanced" : 100,
            "needTime" : 0,
            "needFetch" : 0,
            "saveState" : 2,
            "restoreState" : 2,
            "isEOF" : 1,
            "invalidates" : 0,
            "sortPattern" : {
                "signupTime" : -1
            },
            "memUsage" : 1698112,
            "memLimit" : 33554432,
            "inputStage" : {
                "stage" : "SORT_KEY_GENERATOR",
                "nReturned" : 100,
                "executionTimeMillisEstimate" : 0,
                "works" : 100,
                "advanced" : 100,
                "needTime" : 0,
                "needFetch" : 0,
                "saveState" : 2,
                "restoreState" : 2,
                "isEOF" : 1,
                "invalidates" : 0,
                "inputStage" : {
                    "stage" : "FETCH",
                    "nReturned" : 100,
                    "executionTimeMillisEstimate" : 0,
                    "works" : 100,
                    "advanced" : 100,
                    "needTime" : 0,
                    "needFetch" : 0,
                    "saveState" : 2,
                    "restoreState" : 2,
                    "isEOF" : 1,
                    "invalidates" : 0,
                    "docsExamined" : 100,
                    "alreadyHasObj" : 0,
                    "inputStage" : {
                        "stage" : "IXSCAN",
                        "nReturned" : 100,
                        "executionTimeMillisEstimate" : 0,
                        "works" : 100,
                        "advanced" : 100,
                        "needTime" : 0,
                        "needFetch" : 0,
                        "saveState" : 2,
                        "restoreState" : 2,
                        "isEOF" : 1,
                        "invalidates" : 0,
                        "keyPattern" : {
                            "signupTime" : -1
                        },
                        "indexName" : "signupTime_-1",
                        "isMultiKey" : false,
                        "multiKeyPaths" : {
                            "signupTime" : []
                        },
                        "isUnique" : false,
                        "isSparse" : false,
                        "isPartial" : false,
                        "indexVersion" : 2,
                        "direction" : "backward",
                        "indexBounds" : {
                            "signupTime" : [
                                "[MaxKey, MinKey]"
                            ]
                        },
                        "keysExamined" : 100,
                        "seeks" : 1,
                        "dupsTested" : 0,
                        "dupsDropped" : 0
                    }
                }
            }
        },
        "allPlansExecution" : []
    },
    "serverInfo" : {
        "host" : "localhost",
        "port" : 27017,
        "version" : "4.2.0",
        "gitVersion" : "a4b751dcf51dd249c5865812b390cfd1c0129c30"
    },
    "ok" : 1
}

可以看到,查询的执行计划是一个 JSON 对象,包含了许多信息。其中比较重要的是:

  • queryPlanner:查询规划器,包含了索引策略、优化器等信息。
  • winningPlan:最优执行计划。
  • rejectedPlans:调试用的信息,列出了其他可能的执行计划。
  • executionStats:执行统计,包含了查询的耗时、扫描文档数等信息。

我们可以看到,这个查询使用了 username 字段的索引,并且使用了 -1 方向的 signupTime 索引进行了排序。完成这个查询共扫描了 100 个文档,总共执行了 47ms。可以根据这些信息来优化索引结构、查询方式等,进一步提高查询性能。

如果需要得到更具体的查询统计信息,可以使用 hint() 方法和 explain("allPlansExecution") 方法。

例如,可以使用以下代码对上面的查询加上一个 hint

db.users.find({ username: "pidancode.com" }).sort({ signupTime: -1 }).hint({ username: 1, signupTime: -1 }).explain("allPlansExecution")

这时候输出的结果中将包含每个分片数据库的执行计划和执行统计信息。

除此之外,还可以使用 MongoDB 自带的性能分析工具 mongostatmongotop 来查看 MongoDB 服务器的整体性能表现。

相关文章