MongoDB查询性能分析—— explain 操作返回结果详解
MongoDB 提供 db.collection.explain(), cursort.explain() 及 explain 命令獲取查詢計(jì)劃及查詢計(jì)劃執(zhí)行統(tǒng)計(jì)信息。
explain 結(jié)果將查詢計(jì)劃以階段樹的形式呈現(xiàn)。 每個(gè)階段將其結(jié)果(文檔或索引鍵)傳遞給父節(jié)點(diǎn)。 葉節(jié)點(diǎn)訪問集合或索引。 中間節(jié)點(diǎn)操縱由子節(jié)點(diǎn)產(chǎn)生的文檔或索引鍵。 根節(jié)點(diǎn)是MongoDB從中派生結(jié)果集的最后階段。
階段操作描述,例:
- COLLSCAN 集合掃描
- IXSCAN 索引掃描
- FETCH 檢出文檔
- SHARD_MERGE 合并分片中結(jié)果
- SHARDING_FILTER 分片中過濾掉孤立文檔
- LIMIT 使用limit 限制返回?cái)?shù)
- PROJECTION 使用 skip 進(jìn)行跳過
- IDHACK 針對(duì)_id進(jìn)行查詢
- COUNT 利用db.coll.explain().count()之類進(jìn)行count運(yùn)算
- COUNTSCAN count不使用Index進(jìn)行count時(shí)的stage返回
- COUNT_SCAN count使用了Index進(jìn)行count時(shí)的stage返回
- SUBPLA 未使用到索引的$or查詢的stage返回
- TEXT 使用全文索引進(jìn)行查詢時(shí)候的stage返回
- PROJECTION 限定返回字段時(shí)候stage的返回
- …
explain 操作返回結(jié)果詳解
queryPlanner
queryPlanner 顯示的是被查詢優(yōu)化器選擇出來的查詢計(jì)劃。
以下未分片集合 explain 操作結(jié)果如下:
{"queryPlanner" : {"plannerVersion" : <int>,"namespace" : <string>,"indexFilterSet" : <boolean>,"parsedQuery" : {...},"winningPlan" : {"stage" : <STAGE1>,..."inputStage" : {"stage" : <STAGE2>,..."inputStage" : {...}}},"rejectedPlans" : [<candidate plan 1>,...]}- queryPlanner.namespace 一個(gè)字符串,指定運(yùn)行查詢的命名空間(即.)。
- queryPlanner.indexFilterSet boolan值,表示MongoDB 對(duì)于此query shape 是否使用了索引過濾器。
- queryPlanner.winningPlan 文檔類型,詳細(xì)顯示查詢優(yōu)化程序選擇的查詢計(jì)劃。
- winningPlan.stage 階段名稱。每個(gè)階段都有每個(gè)階段特有的信息。 例如,IXSCAN 階段將包括索引邊界以及特定于索引掃描的其他數(shù)據(jù)。 如果階段具有子階段或多個(gè)子階段,則階段將具有inputStage 或 inputStages。
- winningPlan.inputStage 描述子階段的文檔。它為其父級(jí)提供文檔或索引鍵。 如果父級(jí)只有一個(gè)子級(jí),則該字段存在。
- winningPlan.inputStages 描述子階段的數(shù)組。子階段為父階段提供文檔或索引鍵。 如果父級(jí)具有多個(gè)子節(jié)點(diǎn),則該字段存在。 例如,$or 表達(dá)式或索引交集的階段消耗來自多個(gè)源的輸入。
- queryPlanner.rejectedPlans 查詢優(yōu)化器考慮和拒絕的候選計(jì)劃數(shù)組。 如果沒有其他候選計(jì)劃,則該數(shù)組可以為空。
對(duì)于分片集合,獲勝計(jì)劃包括分每個(gè)訪問的分片的計(jì)劃信息數(shù)組。
executionStats
executionStats 返回的是獲勝計(jì)劃執(zhí)行相關(guān)信息。必須在 executionStats 或 allPlansExecution 詳細(xì)模式下運(yùn)行explain才顯示executionStats 相關(guān)信息。
要捕獲選擇執(zhí)行計(jì)劃期間執(zhí)行相關(guān)信息,必須使用 allPlansExecution 模式運(yùn)行。
executionStats 模式 explain 實(shí)例:
db.products.explain("executionStats").find({ quantity: { $gt: 50 }, category: "apparel" } )allPlansExecution 模式 explain 實(shí)例:
db.products.explain("allPlansExecution").update({ quantity: { $lt: 1000}, category: "apparel" },{ $set: { reorder: true } } )以下未分片集合 explain 操作結(jié)果如下:
"executionStats" : {"executionSuccess" : <boolean>,"nReturned" : <int>,"executionTimeMillis" : <int>,"totalKeysExamined" : <int>,"totalDocsExamined" : <int>,"executionStages" : {"stage" : <STAGE1>"nReturned" : <int>,"executionTimeMillisEstimate" : <int>,"works" : <int>,"advanced" : <int>,"needTime" : <int>,"needYield" : <int>,"isEOF" : <boolean>,..."inputStage" : {"stage" : <STAGE2>,..."nReturned" : <int>,"executionTimeMillisEstimate" : <int>,"keysExamined" : <int>,"docsExamined" : <int>,..."inputStage" : {...}}},"allPlansExecution" : [{ <partial executionStats1> },{ <partial executionStats2> },...] }- executionStats 描述獲勝計(jì)劃完整的查詢執(zhí)行信息。 對(duì)于寫入操作,是指將要執(zhí)行修改的信息,但不會(huì)真實(shí)修改數(shù)據(jù)庫。
- executionStats.nReturned 查詢條件匹配到的文檔數(shù)量。
- executionStats.executionTimeMillis 查詢計(jì)劃選擇和查詢執(zhí)行所需的總時(shí)間(以毫秒為單位)。executionTimeMillis 對(duì)應(yīng)于早期版本的MongoDB中cursor.explain() 返回的millis字段。
- executionStats.totalKeysExamined 掃描的索引條目數(shù)。totalKeysExamined 對(duì)應(yīng)于早期版本的MongoDB中cursor.explain() 返回的 nscanned字段。
- executionStats.totalDocsExamined 掃描的文檔數(shù)。totalDocsExamined 對(duì)應(yīng)于早期版本的MongoDB中cursor.explain() 返回的 nscannedObjects字段。
- executionStats.executionStages 階段樹形式展示獲勝計(jì)劃完整的執(zhí)行信息。即,一個(gè)階段可以有一個(gè)inputStage或多個(gè)inputStages。
-
executionStats.executionStages.works 查詢執(zhí)行階段執(zhí)行的“工作單元”的數(shù)量。 查詢執(zhí)行階段將其工作分為小單元。 “工作單位”可能包括檢查單個(gè)索引鍵,從集合中提取單個(gè)文檔,將投影應(yīng)用于單個(gè)文檔或執(zhí)行內(nèi)部記賬。
-
executionStats.executionStages.advanced 返回到父階段的結(jié)果數(shù)。
-
executionStats.executionStages.needTime 未將中間結(jié)果返回給其父級(jí)的工作循環(huán)數(shù)。 例如,索引掃描階段可以花費(fèi)一個(gè)工作周期來尋找索引中的新位置而不是返回索引關(guān)鍵字; 這個(gè)工作周期將計(jì)入explain.executionStats.executionStages.needTime而不是explain.executionStats.executionStages.advanced。
-
executionStats.executionStages.needYield 存儲(chǔ)層請(qǐng)求查詢系統(tǒng)產(chǎn)生鎖定的次數(shù)。
-
executionStats.executionStages.isEOF 執(zhí)行階段是否已到達(dá)流的結(jié)尾:
- 如果為true或1,則執(zhí)行階段已到達(dá)流末尾。
- 如果為false或0,則階段可能仍會(huì)返回結(jié)果。 例如,有限制的查詢,其執(zhí)行階段包含LIMIT階段,其中查詢的輸入階段為IXSCAN。 如果查詢返回超過指定的限制,LIMIT階段將報(bào)告isEOF:1,但其基礎(chǔ)IXSCAN階段將報(bào)告isEOF:0。
-
executionStats.executionStages.inputStage.keysExamined 對(duì)于掃描索引的查詢執(zhí)行階段(例如IXSCAN),keysExamined是在索引掃描過程中檢查的入站和越界鍵的總數(shù)。 如果索引掃描由單個(gè)連續(xù)范圍的鍵組成,則只需要檢查入站鍵。 如果索引邊界由若干鍵范圍組成,則索引掃描執(zhí)行過程可以檢查越界鍵,以便從一個(gè)范圍的末尾跳到下一個(gè)范圍的開頭。
考慮以下示例,集合包含字段x值為1到100的100個(gè)文檔,其中索引字段為x:
for(var x=1;x<=100;x++){db.keys.insert({x:x}); } db.keys.ensureIndex({x:1}); db.keys.find( { x : { $in : [ 3, 4, 50, 74, 75, 90 ] } } ).explain( "executionStats" )該查詢將掃描鍵3和4.然后它將掃描鍵5,檢測它是否超出界限,并跳到下一個(gè)鍵50。
繼續(xù)該過程,查詢掃描鍵3,4,5,50,51,74,75,76,90和91.鍵5,51,76和91是仍在檢查的越界鍵。 keysExamined的值為10。
-
executionStats.executionStages.inputStage.docsExamined 在查詢執(zhí)行階段掃描的文檔數(shù)。
-
executionStats.allPlansExecution 包含在計(jì)劃選擇階段為獲勝和拒絕計(jì)劃捕獲的部分執(zhí)行信息。 僅當(dāng)explain在allPlansExecution詳細(xì)模式下運(yùn)行時(shí),該字段才會(huì)出現(xiàn)。
-
serverInfo
對(duì)于未分片的集合,MongoDB實(shí)例explain返回以下信息:
"serverInfo" : {"host" : <string>,"port" : <int>,"version" : <string>,"gitVersion" : <string> }對(duì)于分片集合,explain返回每個(gè)訪問的分片的serverInfo。
Sharded Collection
對(duì)于分片集合,explain在shards字段中返回每個(gè)訪問的分片的核心查詢規(guī)劃器和服務(wù)器信息:
{"queryPlanner" : {..."winningPlan" : {..."shards" : [{"shardName" : <shard>,<queryPlanner information for shard>,<serverInfo for shard>},...],},},"executionStats" : {..."executionStages" : {..."shards" : [{"shardName" : <shard>,<executionStats for shard>},...]},"allPlansExecution" : [{"shardName" : <string>,"allPlans" : [ ... ]},...]} }- queryPlanner.winningPlan.shards 每個(gè)訪問分片的queryPlanner和serverInfo的文檔數(shù)組。
- executionStats.executionStages.shards 包含每個(gè)訪問分片的executionStats的文檔數(shù)組。
兼容性的修改
在 3.0 版本中的修改。
explain 結(jié)果的格式和字段與以前的版本相比有所變化。 以下列出了一些主要差異。
集合掃描 VS 索引使用
如果查詢計(jì)劃程序選擇了集合掃描,則說明結(jié)果包括COLLSCAN階段。
如果查詢計(jì)劃程序選擇索引,則說明結(jié)果包括IXSCAN階段。 該階段包括諸如索引key的匹配,遍歷方向和索引邊界之類的信息。
在早期版本的MongoDB中,cursor.explain() 返回了光標(biāo)字段,其值為:
用于集合掃描的BasicCursor,以及索引掃描的BtreeCursor <索引名稱> [<方向>]。
覆蓋查詢
當(dāng)索引覆蓋查詢時(shí),MongoDB可以匹配查詢條件并僅使用索引鍵返回結(jié)果; 即MongoDB不需要檢查集合中的文檔以返回結(jié)果。
當(dāng)索引覆蓋查詢時(shí),解釋結(jié)果的IXSCAN階段不是FETCH階段的后代,而在executionStats中,totalDocsExamined為0。
在早期版本的MongoDB中,cursor.explain() 返回indexOnly字段以指示索引是否覆蓋了查詢。
索引交叉
對(duì)于索引交集計(jì)劃,結(jié)果將包括AND_SORTED階段或AND_HASH階段,其中包含詳細(xì)說明索引的inputStages數(shù)組; 例如。:
{"stage" : "AND_SORTED","inputStages" : [{"stage" : "IXSCAN",...},{"stage" : "IXSCAN",...}] }在以前版本的MongoDB中,cursor.explain() 返回了游標(biāo)字段,其中包含索引交叉點(diǎn)的復(fù)雜計(jì)劃。
$or 表達(dá)式
如果MongoDB使用$or 表達(dá)式的索引,則結(jié)果將包含OR階段,其中包含詳細(xì)說明索引的inputStages數(shù)組; 例如。:
{"stage" : "OR","inputStages" : [{"stage" : "IXSCAN",...},{"stage" : "IXSCAN",...},...] }在早期版本的MongoDB中,cursor.explain() 返回了詳細(xì)索引的子句數(shù)組。
Sort 階段
如果MongoDB可以使用索引掃描來獲取請(qǐng)求的排序順序,則結(jié)果將不包括SORT階段。 否則,如果MongoDB無法使用索引進(jìn)行排序,則解釋結(jié)果將包括SORT階段。
在MongoDB 3.0之前,cursor.explain() 返回scanAndOrder字段以指定MongoDB是否可以使用索引順序返回排序結(jié)果。
總結(jié)
explain 希望看到的階段
Fetch+IDHACK
Fetch+ixscan
Limit+(Fetch+ixscan)
PROJECTION+ixscan
SHARDING_FILTER+ixscan
COUNT_SCAN
…
explain 不希望看到的階段
COLLSCAN(全表掃描),
SORT(使用sort但是無index),
不合理的SKIP,
SUBPLA(未用到index的$or),
COUNTSCAN
來自個(gè)人博客:學(xué)習(xí)園
http://www.xuexiyuan.cn/article/detail/179.html
總結(jié)
以上是生活随笔為你收集整理的MongoDB查询性能分析—— explain 操作返回结果详解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 白鹿原64、65集 黑娃被白孝文活捉
- 下一篇: MongoDB 字段拼接 $concat