做爲近年最爲火熱的文檔型數據庫,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是現版本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" : { "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;
第一部分完。
下一部分將針對IndexFilter與Stage進行分析。
盡請期待。