1. 程式人生 > >MongoDB執行計劃分析詳解(1)

MongoDB執行計劃分析詳解(1)

mongo smu pre als comm 計劃 -- {} direct

正文

queryPlanner

queryPlanner是現版本explain的默認模式,queryPlanner模式下並不會去真正進行query語句查詢,而是針對query語句進行執行計劃分析並選出winning plan。

{
        "queryPlanner" : {
                "plannerVersion" : 1,
                "namespace" : "game_db.game_user",
                "indexFilterSet" : false,
                "parsedQuery" : {
                        "w" : {
                                "$eq" : 1
                        }
                },
                "winningPlan" : {
                        "stage" : "FETCH",
                        "inputStage" : {
                                "stage" : "IXSCAN",
                                "keyPattern" : {
                                        "w" : 1,
                                        "n" : 1
                                },
                                "indexName" : "w_1_n_1",
                                "isMultiKey" : false,
                                "direction" : "forward",
                                "indexBounds" : {
                                        "w" : [
                                                "[1.0, 1.0]"
                                        ],
                                        "n" : [
                                                "[MinKey, MaxKey]"
                                        ]
                                }
                        }
                },
                "rejectedPlans" : [
                        {
                                "stage" : "FETCH",
                                "inputStage" : {
                                        "stage" : "IXSCAN",
                                        "keyPattern" : {
                                                "w" : 1,
                                                "v" : 1
                                        },
                                        "indexName" : "w_1_v_1",
                                        "isMultiKey" : false,
                                        "direction" : "forward",
                                        "indexBounds" : {
                                                "w" : [
                                                        "[1.0, 1.0]"
                                                ],
                                                "v" : [
                                                        "[MinKey, MaxKey]"
                                                ]
                                        }
                                }
                        }
                ]
        },

先來看queryPlanner模式的各個返回意義。

explain.queryPlanner

queryPlanner的返回。

explain.queryPlanner.namespace

顧名思義,該值返回的是該query所查詢的表。

explain.queryPlanner.indexFilterSet

針對該query是否有indexfilter(會在後文進行詳細解釋)。

explain.queryPlanner.winningPlan

查詢優化器針對該query所返回的最優執行計劃的詳細內容。

explain.queryPlanner.winningPlan.stage

最優執行計劃的stage,這裏返回是FETCH,可以理解為通過返回的index位置去檢索具體的文檔(stage有數個模式,將在後文中進行詳解)。

explain.queryPlanner.winningPlan.inputStage

explain.queryPlanner.winningPlan.stage的child stage,此處是IXSCAN,表示進行的是index scanning。

explain.queryPlanner.winningPlan.keyPattern

所掃描的index內容,此處是w:1與n:1。

explain.queryPlanner.winningPlan.indexName

winning plan所選用的index。

explain.queryPlanner.winningPlan.isMultiKey

是否是Multikey,此處返回是false,如果索引建立在array上,此處將是true。

explain.queryPlanner.winningPlan.direction

此query的查詢順序,此處是forward,如果用了.sort({w:-1})將顯示backward。

explain.queryPlanner.winningPlan.indexBounds

winningplan所掃描的索引範圍,此處查詢條件是w:1,使用的index是w與n的聯合索引,故w是[1.0,1.0]而n沒有指定在查詢條件中,故是[MinKey,MaxKey]。

explain.queryPlanner.rejectedPlans

其他執行計劃(非最優而被查詢優化器reject的)的詳細返回,其中具體信息與winningPlan的返回中意義相同,故不在此贅述。

executionStats

executionStats的返回中多了如下:

  "executionStats" : {
                "executionSuccess" : true,
                "nReturned" : 29861,
                "executionTimeMillis" : 23079,
                "totalKeysExamined" : 29861,
                "totalDocsExamined" : 29861,
                "executionStages" : {
                        "stage" : "FETCH",
                        "nReturned" : 29861,
                        "executionTimeMillisEstimate" : 22685,
                        "works" : 29862,
                        "advanced" : 29861,
                        "needTime" : 0,
                        "needFetch" : 0,
                        "saveState" : 946,
                        "restoreState" : 946,
                        "isEOF" : 1,
                        "invalidates" : 0,
                        "docsExamined" : 29861,
                        "alreadyHasObj" : 0,
                        "inputStage" : {
                                "stage" : "IXSCAN",
                                "nReturned" : 29861,
                                "executionTimeMillisEstimate" : 70,
                                "works" : 29862,
                                "advanced" : 29861,
                                "needTime" : 0,
                                "needFetch" : 0,
                                "saveState" : 946,
                                "restoreState" : 946,
                                "isEOF" : 1,
                                "invalidates" : 0,
                                "keyPattern" : {
                                        "w" : 1,
                                        "n" : 1
                                },
                                "indexName" : "w_1_n_1",
                                "isMultiKey" : false,
                                "direction" : "forward",
                                "indexBounds" : {
                                        "w" : [
                                                "[1.0, 1.0]"
                                        ],
                                        "n" : [
                                                "[MinKey, MaxKey]"
                                        ]
                                },
                                "keysExamined" : 29861,
                                "dupsTested" : 0,
                                "dupsDropped" : 0,
                                "seenInvalidated" : 0,
                                "matchTested" : 0
                        }
                }
        },

executionStats模式中,我們主要需要註意的返回有如下幾個

executionStats.executionSuccess

是否執行成功

executionStats.nReturned

查詢的返回條數

executionStats.executionTimeMillis

整體執行時間

executionStats.totalKeysExamined

索引掃描次數

executionStats.totalDocsExamined

document掃描次數

以上幾個非常好理解,我們就不在這裏詳述,後文的案例中會有分析。

executionStats.executionStages.stage

這裏是FETCH去掃描對於documents

executionStats.executionStages.nReturned

由於是FETCH,所以這裏該值與executionStats.nReturned一致

executionStats.executionStages.docsExamined

與executionStats.totalDocsExamined一致

executionStats.inputStage中的與上述理解方式相同

還有一些文檔中沒有描述的返回如:

“works” : 29862,

“advanced” : 29861,

“isEOF” : 1,

這些值都會在explan之初初始化:

mongo/src/mongo/db/exec/plan_stats.h

struct CommonStats {
    CommonStats(const char* type)
        : stageTypeStr(type),
          works(0),
          yields(0),
          unyields(0),
          invalidates(0),
          advanced(0),
          needTime(0),
          needYield(0),
          executionTimeMillis(0),
          isEOF(false) {}

以works為例,查看源碼中發現,每次操作會加1,且會把執行時間記錄在executionTimeMillis中。

mongo/src/mongo/db/exec/fetch.cpp

         ++_commonStats.works;

        // Adds the amount of time taken by work() to executionTimeMillis.
        ScopedTimer timer(&_commonStats.executionTimeMillis);

而在查詢結束EOF,works又會加1,advanced不加。

mongo/src/mongo/db/exec/eof.cpp

PlanStage::StageState EOFStage::work(WorkingSetID* out) {
    ++_commonStats.works;
    // Adds the amount of time taken by work() to executionTimeMillis.
    ScopedTimer timer(&_commonStats.executionTimeMillis);
    return PlanStage::IS_EOF;
}

故正常的返回works會比nReturned多1,這時候isEOF為true(1):

mongo/src/mongo/db/exec/eof.cpp

bool EOFStage::isEOF() {
    return true;
}

unique_ptr<PlanStageStats> EOFStage::getStats() {
    _commonStats.isEOF = isEOF();
    return make_unique<PlanStageStats>(_commonStats, STAGE_EOF);
}

advanced的返回值在命中的時候+1,在skip,eof的時候不會增加如:

mongo/src/mongo/db/exec/skip.cpp

if (PlanStage::ADVANCED == status) {
        // If we‘re still skipping results...
        if (_toSkip > 0) {
            // ...drop the result.
            --_toSkip;
            _ws->free(id);
            ++_commonStats.needTime;
            return PlanStage::NEED_TIME;
        }

        *out = id;
        ++_commonStats.advanced;
        return PlanStage::ADVANCED;

MongoDB執行計劃分析詳解(1)