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


<<:  Day 09: 机器学习你知多少?

>>:  Day13: DockerFile实作Node前後端(上)

Day 9 - HTML

前言 说起HTML, 大家觉得它如何? 相比起JavaScript, HTML不是什麽program...

[Day28]无线耳机与蓝牙技术-组合应用

随着技术的进步,现在的蓝牙相较於早期 除了能达成所需的功耗愈来愈低以外,资料的传输量却能做得愈来愈高...

Day14|【Git】档案管理 - 档案还原 git reset

上篇介绍 git checkout 是还原档案内容,那麽 git reset 的功能是什麽呢? gi...

【终章】 Tailwind VS Bootstrap5 谁能称霸?

这趟 30 天旅程,让我对 CSS Components 与 Utility 的设计上有了更深入了...

[2021铁人赛 Day-01] 前言 and 嵌入式系统简介

前言 大家好,我是 Kevinyu,就像我的参赛题目所描述的 虽然学习资讯领域的相关知识,也碰过树梅...