iT邦幫忙

2021 iThome 鐵人賽

DAY 15
0
Software Development

MongoDB披荊斬棘之路系列 第 15

DAY15 MongoDB Explain 效能分析工具

DAY15 MongoDB Explain 效能分析工具

針對資料庫內的資料進行查詢、新增、刪修都需要迅速地找到該筆資料,因此建立索引很重要。至於要如何評估指令的效能如何,例如參數設計、順序,就需要使用 MongoDB 的 explain 指令,其他資料庫如 Oracle, MSSQL 叫做 execution plan

而 MongoDB 使用語法很簡單

db.collection.find().explain()

MongoDB explain 種類

MongoDB 的 explain 總共有三種模式,分別是:

  • queryPlanner
  • executionStats
  • allPlansExecution

queryPlanner

此模式下,查詢語法會透過內建的 query optimizer 選出最佳的查詢計畫,並且 評估 查詢結果,同時也會列出那些較差的查詢計畫。

無論是查詢或者增刪修,都不會實際修改資料庫的值。

若沒有設定 verbose,此模式為預設模式。

executionStats

此模式下,會根據上述的最佳計畫執行,無論是查詢或增刪修,都會去執行並且取得結果,但是不會真的去改變資料庫的值,這樣做目的當然是告訴你執行的效率如何。

allPlansExecution

基本上就是包含上述兩者。


其實 explain 功能以及呈現內容一直都隨著改版增加,所以你看到的輸出結果不見得會跟網路上其他文章一樣,不過觀念上都是一樣的,記得這點即可。


語法上有兩種方式,像是上面介紹的一種。
在參數方別帶入想要執行的模式 (queryPlanner, executionStats, allPlansExecution)

db.collection.find().explain()
db.collection.find().explain("queryPlanner")
db.collection.find().explain("executionStats")
db.collection.find().explain("allPlansExecution")

或者啟用 verbosity,預設模式就會改為 "allPlansExecution" (但還是可以修改),總之就是看哪個順手了。

db.runCommand(
{
  explain: { querySyntax },
})

or

db.runCommand(
{
  explain: { count: "employee", query: { age: { $gte: 30 } } },
  verbosity: "executionStats"
})

MongoDB explain - queryPlanner

我們先來看看之前文章的範例資料庫中,沒有建立任何 index 情況下,不帶條件查詢的結果如何。

db.employee.find().explain()

{
    "queryPlanner" : {
        "plannerVersion" : 1,
        "namespace" : "hellomongodb.employee", // 查詢的 db 與 collection
        "indexFilterSet" : false, // 後面再講..
        "parsedQuery" : {},      // 查詢條件
        "winningPlan" : {         // 勝出的查詢計畫
            "stage" : "COLLSCAN",  // 關鍵參數,查詢的使用方式
            "direction" : "forward"
        },
        "rejectedPlans" : []
    },
    "serverInfo" : {
        "host" : "ApieMacbook.local",
        "port" : 27666,
        "version" : "5.0.3",
        "gitVersion" : "7ea530946fa7880364d88c8d8b6026bbc9ffa48c"
    },
    "ok" : 1.0
}

serverInfo 屬於一些固定資訊,之後就不再貼上來佔版面。

暫時不解釋差異,我們先替 employee collection 建立以 name 欄位的 index

db.employee.createIndex( { name: 1 } )

再準備第二個查詢語法以及執行計畫,查詢 name 為 Devil

db.employee.find({"name":"Devil"}).explain()

"queryPlanner" : {
        "plannerVersion" : 1,
        "namespace" : "hellomongodb.employee",
        "indexFilterSet" : false,
        "parsedQuery" : {
            "name" : {
                "$eq" : "Devil"
            }
        },
        "winningPlan" : {
            "stage" : "FETCH",
            "inputStage" : {
                "stage" : "IXSCAN",
                "keyPattern" : {
                    "name" : 1.0
                },
                "indexName" : "name_1",
                "isMultiKey" : false,
                "multiKeyPaths" : {
                    "name" : []
                },
                "isUnique" : false,
                "isSparse" : false,
                "isPartial" : false,
                "indexVersion" : 2,
                "direction" : "forward",
                "indexBounds" : {
                    "name" : [
                        "[\"Devil\", \"Devil\"]"
                    ]
                }
            }
        },
        "rejectedPlans" : []
    },
  • parsedQuery 就是你的查詢條件
  • winningPlan 系統選出的查詢,在剛開始不帶任何條件時,結果是這樣
"winningPlan" : {
    "stage" : "COLLSCAN",
    "direction" : "forward"
},

而使用了索引去查詢,會有這樣結果

"winningPlan" : {
"stage" : "FETCH",
"inputStage" : {
    "stage" : "IXSCAN",
    "keyPattern" : {
        "name" : 1.0
    },
    "indexName" : "name_1",
    "isMultiKey" : false,
    "multiKeyPaths" : {
        "name" : []
    },
    "isUnique" : false,
    "isSparse" : false,
    "isPartial" : false,
    "indexVersion" : 2,
    "direction" : "forward",
    "indexBounds" : {
        "name" : [
            "[\"Devil\", \"Devil\"]"
        ]
    }}}

stage 從 COLLSCAN 變成 IXSCAN。
Stage 有以下幾種:

  • COLLSCAN 掃描整個 Collection
  • IXSCAN 根據 Index 進行掃描
  • FETCH 根據 Index 進行掃描資料(Document)
  • SHARD_MERGE 合併各分片(shards)取得的資料
  • SHARDING_FILTER for filtering out orphan documents from shards (這邊我直接使用官方的說明,之後再補上情境)

基本上看到 COLLSCAN 就是完全禁止的,代表你的查詢參數、語法沒辦法有效率的取得資料,原本幾毫秒的查詢可能會變成秒級以上。

所以定期的檢驗所有查詢,看是否有 COLLSCAN 是非常重要的。MongoDB 內建的 profiler 也有相關功能,有興趣也可以找一下之前寫的文章。MongoDB Atlas 也有功能,但沒直接指出來是哪個查詢,有點可惜。


本篇講解了如何使用 explain 指令去分析你的查詢語法,基本上已經非常足夠使用在大部分的索引設計和使用情境,一定要確保在開需求時,使用情境有符合資料庫的設計,否則實作下去了,會受限於技術設計而影響到使用者情境。

下一篇文章再開始講 executionStats 內的項目。


本系列文章會同步發表於我個人的部落格 Pie Note


上一篇
DAY14 MongoDB 索引屬性與進階注意事項
下一篇
DAY16 MongoDB Explain 與 Index 建議
系列文
MongoDB披荊斬棘之路30

尚未有邦友留言

立即登入留言