第1阶段:$Match
{
delFlg: false
}
第2阶段:$lookup
{
from: "parkings",
localField: "parking_id",
foreignField: "_id",
as: "parking",
}
使用2个IDX(ID & parking_ID)。在MongoDB /Linux/ Compass中的解释计划约为9000 ms。在本地MongoDB服务器中的解释计划约为1400 ms。
有什么问题吗?为什么我们的生产服务器运行得这么慢?
完整解释计划
Explain Plan
Query Performance Summary
Documents returned:
71184
Actual query execution time (ms):
8763
Query used the following indexes:
2
_id_
delFlg_1
explainVersion
"1"
stages
Array
serverInfo
Object
serverParameters
Object
command
Object
ok
1
$clusterTime
Object
operationTime
Timestamp({ t: 1666074389, i: 3 })
使用MongoDB 5.0.5版本本地MongoDB 6.0.2版本
1条答案
按热度按时间bihw5rsg1#
不幸的是,提供的
explain
输出大部分被截断了(例如Array
和Object
隐藏了大部分重要信息)。即使没有这些,我们也应该可以做出一些有意义的观察。和往常一样,我建议从更清晰地定义你的最终目标开始。什么被认为是“* 如此缓慢 *”,操作需要多快才能返回,以便为你的用例所接受?如下所述,我怀疑目前的模式是否有很大的改进空间。如果你正在寻找本质上是几个数量级的改进,你可能需要完全重新考虑这种方法。
让我们考虑一下该操作的作用:
false
得{ delFlg: 1 }
索引中得一个条目.FETCH
,从集合中找到完整的文档。parking_id
字段的值,它在parkings
集合上执行{ _id: 1 }
索引的IXSCAN
。如果发现匹配的索引条目,则数据库将从其他集合中FETCH
整个文档,并将其添加到正在生成的新parking
数组字段中。71,183
次 *。使用
8763ms
作为持续时间,这意味着数据库每毫秒执行上述工作的每个完整迭代超过8
次(8.12 ~= 71184 (iters) / 8763 (ms)
)。我认为这听起来很合理,但不太可能是您可以有意义地改进的地方。扩展运行数据库的硬件可能会提供一些增量改进,但是它通常成本很高,不可伸缩,如果您正在寻找更实质性的改进,那么它可能不值得。您在问题中还提到了两个不同的时间和数据库版本。如果我理解正确的话,您提到(explain)操作在
5.0
版本上需要大约9秒,在6.0
版本上大约需要1.4秒。虽然这里提供的信息太少,无法确定,其中一个原因可能与6.0
版本中引入的$lookup
的改进有关。实际上,根据他们的7 Big Reasons to Upgrade to MongoDB 6.0 article,他们声明如下:$lookup的性能也得到了提升,例如,如果外键上有索引,并且匹配了少量文档,$lookup可以比以前快5到10倍得到结果。
这似乎与你的情况和观察非常直接地匹配。
除了升级之外,还有什么可以提高性能呢?如果源集合中的许多/大多数文档都有
{ delFlg: false }
,那么您可能希望去掉{ delFlg: 1 }
索引。当检索的结果相对于集合中数据的整体大小很小时,索引会提供一些好处。但是随着百分比的增加,扫描大部分索引 * 加上 * 随机快速获取所有数据的开销比直接扫描整个集合的效率要低。注解中提到invoices
集合包含70 k个文档,因此delFlg
上的这个 predicate 似乎根本没有删除任何结果。另一个非常突出的问题是评论中的这句话“parkings contains 16 documents"。是否应该将这16个文档中的信息直接移到
parkings
文档中?如果这两个文档经常一起访问,并且parkings
信息不经常更改,那么将两者结合将减少大量开销并将进一步提高性能。关于
explain
,还有两个值得记住的组件:explain
将测量从运行操作到完成所需的时间。由于在聚合管道中没有阻塞阶段,因此我们希望在更短的时间内将初始文档批返回到客户端。对于5.0
,除网络连接外,该时间可能约为12ms
(约为(101 docs / 71184 ) * 8763
)。