MongoDB $explain与$hint:查询分析

 
查询分析是衡量数据库和索引设计有效性的一个非常重要的方式。下面我们来介绍一下比较常用的 $explain 和 $hint 查询。

$explain

$explain 运算符提供了有关查询、索引使用以及查询统计的相关信息,这在索引优化方面非常有用。《MongoDB覆盖索引查询》一节中我们已经使用以下代码在 users 集合中的 gender 和 name 字段上的创建了索引:
> db.users.createIndex({gender:1, name:1})
{
        "createdCollectionAutomatically" : false,
        "numIndexesBefore" : 1,
        "numIndexesAfter" : 2,
        "ok" : 1
}
在 mongo shell 中,您可以通过 $explain 的辅助方法 explain() 来检索查询的相关信息:
> db.users.find({gender:"M"}, {name:1,_id:0}).explain()
上面的 explain() 查询会返回以下分析结果:
{
        "queryPlanner" : {
                "plannerVersion" : 1,
                "namespace" : "bianchengbang.users",
                "indexFilterSet" : false,
                "parsedQuery" : {
                        "gender" : {
                                "$eq" : "M"
                        }
                },
                "winningPlan" : {
                        "stage" : "PROJECTION",
                        "transformBy" : {
                                "name" : 1,
                                "_id" : 0
                        },
                        "inputStage" : {
                                "stage" : "IXSCAN",
                                "keyPattern" : {
                                        "gender" : 1,
                                        "name" : 1
                                },
                                "indexName" : "gender_1_name_1",
                                "isMultiKey" : false,
                                "multiKeyPaths" : {
                                        "gender" : [ ],
                                        "name" : [ ]
                                },
                                "isUnique" : false,
                                "isSparse" : false,
                                "isPartial" : false,
                                "indexVersion" : 2,
                                "direction" : "forward",
                                "indexBounds" : {
                                        "gender" : [
                                                "[\"M\", \"M\"]"
                                        ],
                                        "name" : [
                                                "[MinKey, MaxKey]"
                                        ]
                                }
                        }
                },
                "rejectedPlans" : [ ]
        },
        "serverInfo" : {
                "host" : "LAPTOP-MDE57TIS",
                "port" : 27017,
                "version" : "4.0.10",
                "gitVersion" : "c389e7f69f637f7a1ac3cc9fae843b635f20b766"
        },
        "ok" : 1
}
关于上面的运行结果,有以下几点需要说明:
  • indexOnly:若字段的值为 true,则表示此查询中使用了索引;
  • cursor:指定使用的游标类型,BTreeCursor 类型表示使用了索引,还提供了所用索引的名称,BasicCursor 表示在不使用任何索引的情况下进行了完全扫描;
  • n:表示返回的匹配文档数;
  • nscannedObjects:表示扫描文档的总数;
  • nscanned:表示扫描的文档或索引条目的总数。

$hint

$hint 运算符(也叫“强制查询优化器”)能够使用指定的索引来进行查询,以此来测试查询的性能。当您想要测试具有不同索引的查询性能时,此功能特别有用。在 mongo shell 中您可以使用 $hint 的辅助方法 hint() 来使用此功能,例如下面的查询指定了要使用 gender 和 name 字段的索引:
> db.users.find({gender:"M"},{name:1, _id:0}).hint({gender:1, name:1})
{ "name" : "bianchengbang" }
使用 explain() 来分析以上查询:
> db.users.find({gender:"M"},{name:1, _id:0}).hint({gender:1, name:1}).explain()
{
        "queryPlanner" : {
                "plannerVersion" : 1,
                "namespace" : "bianchengbang.users",
                "indexFilterSet" : false,
                "parsedQuery" : {
                        "gender" : {
                                "$eq" : "M"
                        }
                },
                "winningPlan" : {
                        "stage" : "PROJECTION",
                        "transformBy" : {
                                "name" : 1,
                                "_id" : 0
                        },
                        "inputStage" : {
                                "stage" : "IXSCAN",
                                "keyPattern" : {
                                        "gender" : 1,
                                        "name" : 1
                                },
                                "indexName" : "gender_1_name_1",
                                "isMultiKey" : false,
                                "multiKeyPaths" : {
                                        "gender" : [ ],
                                        "name" : [ ]
                                },
                                "isUnique" : false,
                                "isSparse" : false,
                                "isPartial" : false,
                                "indexVersion" : 2,
                                "direction" : "forward",
                                "indexBounds" : {
                                        "gender" : [
                                                "[\"M\", \"M\"]"
                                        ],
                                        "name" : [
                                                "[MinKey, MaxKey]"
                                        ]
                                }
                        }
                },
                "rejectedPlans" : [ ]
        },
        "serverInfo" : {
                "host" : "LAPTOP-MDE57TIS",
                "port" : 27017,
                "version" : "4.0.10",
                "gitVersion" : "c389e7f69f637f7a1ac3cc9fae843b635f20b766"
        },
        "ok" : 1
}