当你第一眼看到 explain 和 hint 的时候,第一个反应就是 mysql 中所谓的这两个关键词,确实可以看出,这个就是在 mysql 中借鉴过来的,既然是借鉴
过来的,我想大家都知道这两个关键字的用处,话不多说,速速观看~~~
一:explain 演示
1. 构建数据
为了方便演示,我需要 create ten data to inventory,而且还是要在 no index 的情况下,比如下面这样:
1 db.inventory.insertMany([2 {
"_id": 1,
"item": "f1",
type: "food",
quantity: 500
},
3 {
"_id": 2,
"item": "f2",
type: "food",
quantity: 100
},
4 {
"_id": 3,
"item": "p1",
type: "paper",
quantity: 200
},
5 {
"_id": 4,
"item": "p2",
type: "paper",
quantity: 150
},
6 {
"_id": 5,
"item": "f3",
type: "food",
quantity: 300
},
7 {
"_id": 6,
"item": "t1",
type: "toys",
quantity: 500
},
8 {
"_id": 7,
"item": "a1",
type: "apparel",
quantity: 250
},
9 {
"_id": 8,
"item": "a2",
type: "apparel",
quantity: 400
},
10 {
"_id": 9,
"item": "t2",
type: "toys",
quantity: 50
},
11 {
"_id": 10,
"item": "f4",
type: "food",
quantity: 75
}]);
2. 无索引查询
db.inventory.find({
quantity: {
$gte: 100,
$lte: 200
}
}).explain("executionStats")
从上图中,我们看到了三个圈圈,这些都是我们在 find 中非常重要的信息,具体信息解释如下:
<1>COLLSCAN
这个是什么意思呢? 如果你仔细一看,应该知道就是 CollectionScan,就是所谓的 "集合扫描",对不对,看到集合扫描是不是就可以直接 map 到
数据库中的 table scan/heap scan 呢??? 是的,这个就是所谓的性能最烂最无奈的由来.
<2> nReturned
这个很简单,就是所谓的 numReturned,就是说最后返回的 num 个数,从图中可以看到,就是最终返回了三条...
<3> docsExamined
那这个是什么意思呢??就是 documentsExamined,检查了 10 个 documents...而从返回上面的 nReturned...
ok,那从上面三个信息中,我们可以得出,原来我 examine 10 条数据,最终才返回 3 条,说明做了 7 条数据 scan 的无用功,那么这个时候问题就来了,
如何减少 examine 的 documents...
完整的 plans 如下:
/* 1 */
{
"queryPlanner": {
"plannerVersion": 1,
"namespace": "datamip.inventory",
"indexFilterSet": false,
"parsedQuery": {
"$and": [{
"quantity": {
"$lte": 200.0
}
},
{
"quantity": {
"$gte": 100.0
}
}]
},
"winningPlan": {
"stage": "COLLSCAN",
"filter": {
"$and": [{
"quantity": {
"$lte": 200.0
}
},
{
"quantity": {
"$gte": 100.0
}
}]
},
"direction": "forward"
},
"rejectedPlans": []
},
"executionStats": {
"executionSuccess": true,
"nReturned": 3,
"executionTimeMillis": 1,
"totalKeysExamined": 0,
"totalDocsExamined": 10,
"executionStages": {
"stage": "COLLSCAN",
"filter": {
"$and": [{
"quantity": {
"$lte": 200.0
}
},
{
"quantity": {
"$gte": 100.0
}
}]
},
"nReturned": 3,
"executionTimeMillisEstimate": 0,
"works": 12,
"advanced": 3,
"needTime": 8,
"needYield": 0,
"saveState": 0,
"restoreState": 0,
"isEOF": 1,
"invalidates": 0,
"direction": "forward",
"docsExamined": 10
}
},
"serverInfo": {
"host": "localhost.localdomain",
"port": 27017,
"version": "3.2.8",
"gitVersion": "ed70e33130c977bda0024c125b56d159573dbaf0"
},
"ok": 1.0
}
View Code
3. 使用 single field 加速查找
知道前因后果之后,我们就可以进行针对性的建立索引,比如在 quality 字段之上,如下:
db.inventory.createIndex({
quantity: 1
}) db.inventory.find({
quantity: {
$gte: 100,
$lte: 200
}
}).explain("executionStats")
好了,这时候就有意思了,当我们执行完 createindex 之后,再次 explain,4 个重要的 parameters 就漂下来了:
<1> IXSCAN
这个时候再也不是所谓的 COLLSCAN 了,而是 IndexScan,这就说明我们已经命中索引了.
<2> nReturned,totalDocsExamined,totalKeysExamined
从图中可以看到三个参数都是 3,这就说明我们的 mongodb 查看了 3 个 key,3 个 document,返回 3 个文档,这个就是所谓的高性能所在,对吧.
二:hint 演示
说到 hint,我想大家也是知道的,很好玩的一个东西,就是用来 force mongodb to excute special index,对吧,为了方便演示,我们做两组复合索
引,比如这次我们在 quality 和 type 上构建一下:
building 完成之后,我们故意这一个这样的查询,针对 quantity 是一个范围,而 type 是一个定值的情况下,我们 force mongodb 去使用 quantity 开头
的复合索引,从而强制 mongodb give up 那个以 {type:1,quantity:1} 的复合索引,很有意思哦,比如下图:
从图中,可以看到,我们检查了 6 个 keys,而从最终找到了 2 个文档,现在我们就知道了,2 和 6 之间还是有不足的地方等待我们去优化了,对吧,下面
我们不 hint 来看一下 mongodb 的最优的 plan 是怎么样的.
再看上面的图,你应该明白了,mongodb 果然执行了那个最优的 plan,是不是很好玩,好了,本篇就说到这里,希望对你有帮助~
来源: