MongoDB乾貨系列2-MongoDB執行計劃分析詳解(1)

寫在以前的話

做爲近年最爲火熱的文檔型數據庫,MongoDB受到了愈來愈多人的關注,可是因爲國內的MongoDB相關技術分享屈指可數,很多朋友向我抱怨無從下手。數據庫

《MongoDB乾貨系列》將從實際應用的角度來進行MongoDB的一些列乾貨的分享,將覆蓋調優,troubleshooting等方面,但願能對你們帶來幫助。架構

若是但願瞭解更多MongoDB基礎的信息,還請你們Google下。fetch

要保證數據庫處於高效、穩定的狀態,除了良好的硬件基礎、高效高可用的數據庫架構、貼合業務的數據模型以外,高效的查詢語句也是不可少的。那麼,如何查看並判斷咱們的執行計劃呢?咱們今天就來談論下MongoDB的執行計劃分析。優化

引子

MongoDB 3.0以後,explain的返回與使用方法與以前版本有了很多變化,介於3.0以後的優秀特點,本文僅針對MongoDB 3.0+的explain進行討論。spa

現版本explain有三種模式,分別以下:rest

  • queryPlannercode

  • executionStats索引

  • allPlansExecutionip

因爲文章字數緣由,本系列將分爲三個部分。
第一部分
第二部分
第三部分文檔

本文是第一部分,主要針對queryPlanner,與executionStats進行分析。

正文

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;

後文

第一部分完。

下一部分將針對IndexFilterStage進行分析。

盡請期待。

相關文章
相關標籤/搜索