针对资料库内的资料进行查询、新增、删修都需要迅速地找到该笔资料,因此建立索引很重要。至於要如何评估指令的效能如何,例如参数设计、顺序,就需要使用 MongoDB 的 explain
指令,其他资料库如 Oracle, MSSQL 叫做 execution plan
。
而 MongoDB 使用语法很简单
db.collection.find().explain()
MongoDB 的 explain 总共有三种模式,分别是:
此模式下,查询语法会透过内建的 query optimizer
选出最佳的查询计画,并且 评估
查询结果,同时也会列出那些较差的查询计画。
无论是查询或者增删修,都不会实际修改资料库的值。
若没有设定 verbose,此模式为预设模式。
此模式下,会根据上述的最佳计画执行,无论是查询或增删修,都会去执行并且取得结果,但是不会真的去改变资料库的值,这样做目的当然是告诉你执行的效率如何。
基本上就是包含上述两者。
其实 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"
})
我们先来看看之前文章的范例资料库中,没有建立任何 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
扫描整个 CollectionIXSCAN
根据 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
>>: Day13: DockerFile实作Node前後端(上)
前言 说起HTML, 大家觉得它如何? 相比起JavaScript, HTML不是什麽program...
随着技术的进步,现在的蓝牙相较於早期 除了能达成所需的功耗愈来愈低以外,资料的传输量却能做得愈来愈高...
上篇介绍 git checkout 是还原档案内容,那麽 git reset 的功能是什麽呢? gi...
这趟 30 天旅程,让我对 CSS Components 与 Utility 的设计上有了更深入了...
前言 大家好,我是 Kevinyu,就像我的参赛题目所描述的 虽然学习资讯领域的相关知识,也碰过树梅...