zoukankan      html  css  js  c++  java
  • MongoDB干货系列2-MongoDB执行计划分析详解(2)(转载)

    写在之前的话

    作为近年最为火热的文档型数据库,MongoDB受到了越来越多人的关注,但是由于国内的MongoDB相关技术分享屈指可数,不少朋友向我抱怨无从下手。

    《MongoDB干货系列》将从实际应用的角度来进行MongoDB的一些列干货的分享,将覆盖调优,troubleshooting等方面,希望能对大家带来帮助。

    如果希望了解更多MongoDB基础的信息,还请大家Google下。

    要保证数据库处于高效、稳定的状态,除了良好的硬件基础、高效高可用的数据库架构、贴合业务的数据模型之外,高效的查询语句也是不可少的。那么,如何查看并判断我们的执行计划呢?我们今天就来谈论下MongoDB的执行计划分析。

    引子

    MongoDB 3.0之后,explain的返回与使用方法与之前版本有了不少变化,介于3.0之后的优秀特色,本文仅针对MongoDB 3.0+的explain进行讨论。

    现版本explain有三种模式,分别如下:

    • queryPlanner

    • executionStats

    • allPlansExecution

    由于文章字数原因,本系列将分为三个部分。
    第一部分
    第二部分
    第三部分
    本文是第二部分,主要是对IndexFilterStage进行分析。

    正文

    IndexFilter

    IndexFilter决定了查询优化器对于某一类型的查询将如何使用index,indexFilter仅影响查询优化器对于该类查询可以用尝试哪些index的执行计划分析,查询优化器还是根据分析情况选择最优计划。

    如果某一类型的查询设定了IndexFilter,那么执行时通过hint指定了其他的index,查询优化器将会忽略hint所设置index,仍然使用indexfilter中设定的查询计划。

    IndexFilter可以通过命令移除,也将在实例重启后清空。

    IndexFilter的创建

    可以通过如下命令为某一个collection建立indexFilter

    db.runCommand(
       {
          planCacheSetFilter: <collection>,
          query: <query>,
          sort: <sort>,
          projection: <projection>,
          indexes: [ <index1>, <index2>, ...]
       }
    )
    
    db.runCommand(
       {
          planCacheSetFilter: "orders",
          query: { status: "A" },
          indexes: [
             { cust_id: 1, status: 1 },
             { status: 1, order_date: -1 }
          ]
       }
    )
    

    上图针对orders表建立了一个indexFilter,indexFilter指定了对于orders表只有status条件(仅对status进行查询,无sort等)的查询的indexes,故下图的查询语句的查询优化器仅仅会从{cust_id:1,status:1}{status:1,order_date:-1}中进行winning plan的选择

    db.orders.find( { status: "D" } )
    db.orders.find( { status: "P" } )
    
    indexFilter的列表

    可以通过如下命令展示某一个collecton的所有indexFilter

    db.runCommand( { planCacheListFilters: <collection> } )
    
    indexFilter的删除

    可以通过如下命令对IndexFilter进行删除

    db.runCommand(
       {
          planCacheClearFilters: <collection>,
          query: <query pattern>,
          sort: <sort specification>,
          projection: <projection specification>
       }
    )
    

    Stage的意义

    explain.queryPlanner.winningPlan.stageexplain.queryPlanner.winningPlan.inputStage等。

    文档中仅有如下几类介绍

    COLLSCAN

    全表扫描

    IXSCAN

    索引扫描

    FETCH

    根据索引去检索指定document

    SHARD_MERGE

    将各个分片返回数据进行merge

    但是根据源码中的信息,个人还总结了文档中没有的如下几类(常用如下,由于是通过源码查找,可能有所遗漏)

    SORT

    表明在内存中进行了排序(与老版本的scanAndOrder:true一致)

    LIMIT

    使用limit限制返回数

    SKIP

    使用skip进行跳过

    IDHACK

    针对_id进行查询

    SHARDING_FILTER

    通过mongos对分片数据进行查询

    COUNT

    利用db.coll.explain().count()之类进行count运算

    COUNTSCAN

    count不使用用Index进行count时的stage返回

    COUNT_SCAN

    count使用了Index进行count时的stage返回

    SUBPLA

    未使用到索引的$or查询的stage返回

    TEXT

    使用全文索引进行查询时候的stage返回

    PROJECTION

    限定返回字段时候stage的返回

    部分源码如下:

    mongo/jstests/libs/analyze_plan.js

    /**
     * A query is covered iff it does *not* have a FETCH stage or a COLLSCAN.
     *
     * Given the root stage of explain's BSON representation of a query plan ('root'),
     * returns true if the plan is index only. Otherwise returns false.
     */
    function isIndexOnly(root) {
        return !planHasStage(root, "FETCH") && !planHasStage(root, "COLLSCAN");
    }
    
    /**
     * Returns true if the BSON representation of a plan rooted at 'root' is using
     * an index scan, and false otherwise.
     */
    function isIxscan(root) {
        return planHasStage(root, "IXSCAN");
    }
    
    /**
     * Returns true if the BSON representation of a plan rooted at 'root' is using
     * the idhack fast path, and false otherwise.
     */
    function isIdhack(root) {
        return planHasStage(root, "IDHACK");
    }
    
    /**
     * Returns true if the BSON representation of a plan rooted at 'root' is using
     * a collection scan, and false otherwise.
     */
    function isCollscan(root) {
        return planHasStage(root, "COLLSCAN");
    }
    
    /**
     * Get the number of chunk skips for the BSON exec stats tree rooted at 'root'.
     */
    function getChunkSkips(root) {
        if (root.stage === "SHARDING_FILTER") {
            return root.chunkSkips;
        }
        else if ("inputStage" in root) {
            return getChunkSkips(root.inputStage);
        }
        else if ("inputStages" in root) {
            var skips = 0;
            for (var i = 0; i < root.inputStages.length; i++) {
                skips += getChunkSkips(root.inputStages[0]);
            }
            return skips;
        }
    

    mongo/jstests/concurrency/fsm_workloads/explain_count.js

    $config.states = Object.extend({
            explainBasicCount: function explainBasicCount(db, collName) {
                var res = db[collName].explain().count();
                assertAlways.commandWorked(res);
                assertAlways(planHasStage(res.queryPlanner.winningPlan, 'COUNT'));
            },
            explainCountHint: function explainCountHint(db, collName) {
                assertWhenOwnColl(function() {
                    var res = db[collName].explain()
                                          .find({ i: this.nInserted / 2 })
                                          .hint({ i: 1 }).count();
                    assertWhenOwnColl.commandWorked(res);
                    assertWhenOwnColl(planHasStage(res.queryPlanner.winningPlan, 'COUNT'));
                    assertWhenOwnColl(planHasStage(res.queryPlanner.winningPlan, 'COUNT_SCAN'));
                });
    

    mongo/jstests/concurrency/fsm_workloads/explain_find.js

    $config.states = Object.extend({
            explainLimit: function explainLimit(db, collName) {
                var res = db[collName].find().limit(3).explain();
                assertAlways.commandWorked(res);
                assertAlways(planHasStage(res.queryPlanner.winningPlan, 'LIMIT'));
            },
            explainBatchSize: function explainBatchSize(db, collName) {
                var res = db[collName].find().batchSize(3).explain();
                assertAlways.commandWorked(res);
            },
            explainAddOption: function explainAddOption(db, collName) {
                var res = db[collName].explain().find().addOption(DBQuery.Option.exhaust).finish();
                assertAlways.commandWorked(res);
            },
            explainSkip: function explainSkip(db, collName) {
                var res = db[collName].explain().find().skip(3).finish();
                assertAlways.commandWorked(res);
                assertAlways(planHasStage(res.queryPlanner.winningPlan, 'SKIP'));
            },
            explainSort: function explainSort(db, collName) {
                var res = db[collName].find().sort({ i: -1 }).explain();
                assertAlways.commandWorked(res);
                assertAlways(planHasStage(res.queryPlanner.winningPlan, 'SORT'));
            },
    
    /**
     * The SubplanStage is used for rooted $or queries. It plans each clause of the $or
     * individually, and then creates an overall query plan based on the winning plan from
     * each clause.
     *
     * Uses the MultiPlanStage in order to rank plans for the individual clauses.
     *
     * Notes on caching strategy:
     *
     *   --Interaction with the plan cache is done on a per-clause basis. For a given clause C,
     *   if there is a plan in the cache for shape C, then C is planned using the index tags
     *   obtained from the plan cache entry. If no cached plan is found for C, then a MultiPlanStage
     *   is used to determine the best plan for the clause; unless there is a tie between multiple
     *   candidate plans, the winner is inserted into the plan cache and used to plan subsequent
     *   executions of C. These subsequent executions of shape C could be either as a clause in
     *   another rooted $or query, or shape C as its own query.
     *
     *   --Plans for entire rooted $or queries are neither written to nor read from the plan cache.
     */
    

    allPlansExecution

    顾名思义,allPlansExecution模式是将所有的执行计划均进行executionStats模式的操作,不在此赘述了。

    转载自:http://www.mongoing.com/eshu_explain2

  • 相关阅读:
    android应用中增加权限判断
    android接收短信——framework处理流程(android 5.1)
    Java 集合系列03之 ArrayList详细介绍(源码解析)和使用示例
    Java 集合系列02之 Collection架构
    Java 集合系列01之 总体框架
    远离职场中的舒适区
    中国式父母,到底缺少些什么?
    孙悟空:越风光,越可怜
    Android学习—RecyclerView的使用(2)
    Android学习—RecyclerView的使用(1)
  • 原文地址:https://www.cnblogs.com/xibuhaohao/p/12066568.html
Copyright © 2011-2022 走看看