资讯专栏INFORMATION COLUMN

高性能mongodb之应用程序跑执行计划

derek_334892 / 1001人阅读

摘要:执行计划之前发了一篇关于执行计划的说明。利用执行计划,我们可以判断每一次的执行情况和给出的执行建议。在中跑执行计划的命令,举个例子执行计划的模式为三种。程序中跑执行计划我使用的是库用的是。响应结果会有执行时间扫描记录数等真实的执行情况。

执行计划

之前发了一篇关于mongodb执行计划的说明。利用执行计划,我们可以判断每一次sql的执行情况和mongodb给出的执行建议。在mongo shell中跑执行计划的命令,举个例子:

db.collecitonName.find({}).explain("queryPlanner")

执行计划的模式为三种:queryPlanner executionStats allPlansExecution。第一种不会真正跑命令本身,只有响应命令分析后的报告。上面例子的响应结果就是对 db.collecitonName.find({}) 这个查询语句的分析。

程序中跑执行计划

我使用的是java, mongodb库用的是mongodb-java-driver。mongodb-java-driver的API提供了两种方式去跑执行计划:

方式一:
MongoClient mongoClient = new MongoClient(new ServerAddress(host, port));
mongoClient.getDB("xxx").getCollection("yyy").find(quert).explain();

这是一个便捷的方式。这种方式会真正执行命令,也就是说它使用的是executionStats模式。响应结果会有执行时间、扫描记录数等真实的执行情况。如果你的程序想要在命令执行前做一个预判,这个API不是你想要的。

方式二:

API没有提供queryPlanner的方式。我花了一些时间去搜索资料,发现网上没有跑queryPlanner的需求,至少我是没有找到类似的发问和使用例子。纠结了一会儿,最终发现库里有这样一个api, mongoClient.getDB("xxx").command(BasicDBObject command),支持程序传入一个命令。最后在官方文档里找到了这样一个说明:

explain

New in version 3.0.

The explain command provides information on the execution of the following commands: aggregate, count, distinct, group, find, findAndModify, delete, and update.

Although MongoDB provides the explain command, the preferred method for running explain is to use the db.collection.explain() and cursor.explain() helpers.

The explain command has the following syntax:

语法如下:

{
   explain: ,
   verbosity: 
}

explain: 。 支持  aggregate, count, distinct, group, find, findAndModify, delete, and update等等的命令。
verbosity: 。支持模式"queryPlanner" 、"executionStats"  、"allPlansExecution" (Default)

跟踪find进去,find支持的字段如下,应有尽有。

{
   "find": ,
   "filter": ,
   "sort": ,
   "projection": ,
   "hint": ,
   "skip": ,
   "limit": ,
   "batchSize": ,
   "singleBatch": ,
   "comment": ,
   "maxScan": ,   // Deprecated in MongoDB 4.0
   "maxTimeMS": ,
   "readConcern": ,
   "max": ,
   "min": ,
   "returnKey": ,
   "showRecordId": ,
   "tailable": ,
   "oplogReplay": ,
   "noCursorTimeout": ,
   "awaitData": ,
   "allowPartialResults": ,
   "collation": 
}

通过阅读文档,跑queryPlanner模式的执行计划应该是这样的:

//查询某个集合,queryCondition是查询条件。

MongoClient mongoClient = MongoUtil.getConnection(mongodb.getHost(), mongodb.getPort(), "", "", mongodb.getDb());
BasicDBObject command = new BasicDBObject();
BasicDBObject find = new BasicDBObject();
find.put("find", "集合名");
find.put("filter", queryCondition);//查询条件,是一个BasicDBObject
command.put("explain", find);
command.put("verbosity", "queryPlanner");
CommandResult explainResult = mongoClient.getDB(mongodb.getDb()).command(command);
python程序中跑执行计划遇到的坑

使用 pymongo库

import json
import pymongo

if __name__ == "__main__":
    client = pymongo.MongoClient(host="127.0.0.1", port=27017)
    #指定一个db
    db = client.get_database(name="datanamexxx")

    command = {}
    explain = {}
    #要操作的集合
    explain["find"] = "collectionnamexxx"
    #查询的条件
    explain["filter"] = {"col1":"202060056"}
    verbosity = "executionStats"
    command["explain"] = explain
    command["verbosity"] = verbosity
    print json.dumps(db.command(command=command))

以上程序是有问题的,不能达到想要的目的(一次查询的执行情况)。后来经过查阅mongo文档和尝试,明确是使用方式不正确导致的。
错误原因:mongo的command要求参数是有序的,因为首参数是命令名。正如上面的find命令:

{
   "find": , #命令名
   "filter": ,
   "sort": ,
   "projection": ,
   "hint": ,
   "skip": ,
   "limit": ,
   "batchSize": ,
   "singleBatch": ,
    ...

mongo驱动在处理命令时首先要知道执行哪个命令,然而 python的dict或者的java的map再或者所有的map数据结构都是无序的。我们需要一个记录参数的顺序,使用者需要把首参数设置在最前面。我们来看看驱动的源码,原理其实是对dict封装一层,添加一个list来记录参数顺序:

#继承dict
class SON(dict):
    def __init__(self, data=None, **kwargs):
        #__keys就是记录参数顺序的列表
        self.__keys = []
        dict.__init__(self)
        self.update(data)
        self.update(kwargs)
    #省略...
    #打印时,按__keys的顺序拼字符串,合理
    def __repr__(self):
        result = []
        for key in self.__keys:
            result.append("(%r, %r)" % (key, self[key]))
        return "SON([%s])" % ", ".join(result)

    #设置元素时,先把key按顺序保存下来
    def __setitem__(self, key, value):
        if key not in self.__keys:
            self.__keys.append(key)
        dict.__setitem__(self, key, value)

    def __delitem__(self, key):
        self.__keys.remove(key)
        dict.__delitem__(self, key)

    #省略...
pymongo正确的使用方式
import json
import pymongo

if __name__ == "__main__":
    client = pymongo.MongoClient(host="127.0.0.1", port=27017)
    #指定一个db
    db = client.get_database(name="datanamexxx")
    
    #注意顺序
    explainSon = SON([("find", "collectionnamexxx"),
               ("filter", {"uid": "202060056"})])
    cmd = SON([("explain", explainSon),
               ("verbosity", "queryPlanner")])
    print json.dumps(db.command(cmd))

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/19496.html

相关文章

  • 性能mongodb执行计划

    我的专栏地址:我的segmentfault,欢迎浏览 一、执行计划介绍 MongoDB 3.0之后,explain的返回与使用方法与之前版本有了不少变化,介于3.0之后的优秀特色,本文仅针对MongoDB 3.0+的explain进行讨论。现版本explain有三种模式,分别如下: queryPlanner executionStats allPlansExecution 其中 qu...

    senntyou 评论0 收藏0
  • 2017年3月份前端资源分享

    平日学习接触过的网站积累,以每月的形式发布。2017年以前看这个网址:http://www.kancloud.cn/jsfron... 03月份前端资源分享 1. Javascript 175453545 Redux compose and middleware 源码分析 深入 Promise(二)——进击的 Promise Effective JavaScript leeheys blog -...

    ermaoL 评论0 收藏0
  • 2017年3月份前端资源分享

    平日学习接触过的网站积累,以每月的形式发布。2017年以前看这个网址:http://www.kancloud.cn/jsfron... 03月份前端资源分享 1. Javascript 175453545 Redux compose and middleware 源码分析 深入 Promise(二)——进击的 Promise Effective JavaScript leeheys blog -...

    kamushin233 评论0 收藏0
  • 2017年3月份前端资源分享

    平日学习接触过的网站积累,以每月的形式发布。2017年以前看这个网址:http://www.kancloud.cn/jsfron... 03月份前端资源分享 1. Javascript 175453545 Redux compose and middleware 源码分析 深入 Promise(二)——进击的 Promise Effective JavaScript leeheys blog -...

    yy736044583 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<