MongoDB執行計劃分析詳解(1)
正文
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)