所以我在mongoDB中有以下查询:
[
{
$project: {
_id: 0,
id: 1,
uniqueId: 1,
shortLabel: 1,
"geographicLocation.id": 1,
"economicConcept.id": 1,
"frequency.id": 1,
"scale.id": 1,
"unit.id": 1,
source: { $arrayElemAt: ["$source", 0] },
},
},
{
$lookup: {
from: "geographic_location",
localField: "geographicLocation.id",
foreignField: "id",
as: "region",
},
},
{
$match: {
$and: [
{
$or: [
{ "region.name": "United States" },
],
},
],
},
},
{
$facet: {
metadata: [
{ $count: "total" },
{ $addFields: { page: 1 } },
],
data: [{ $skip: 0 }, { $limit: 10 }],
},
},
]
然而,对于D(200k)中的记录量来说,它相当慢。我正在尝试改进这个查询,但我不确定为什么方法不起作用。
我已经在运行查找的geographic_location
集合上的id
上添加了索引。但它似乎没有使用此索引,而是使用COLSCAN
策略。Match
阶段也太慢,但我再次放置了索引。
下面是查询的解释结果的外观:
{
"stages": [
{
"$cursor": {
"queryPlanner": {
"plannerVersion": 1,
"namespace": "ihs_markit_data.unique_ids_in_each_bank",
"indexFilterSet": false,
"parsedQuery": {},
"queryHash": "57975262",
"planCacheKey": "57975262",
"winningPlan": {
"stage": "PROJECTION_DEFAULT",
"transformBy": {
"uniqueId": true,
"shortLabel": true,
"id": true,
"geographicLocation": {
"id": true
},
"_id": false
},
"inputStage": {
"stage": "COLLSCAN",
"direction": "forward"
}
},
"rejectedPlans": []
},
"executionStats": {
"executionSuccess": true,
"nReturned": 230045,
"executionTimeMillis": 12134,
"totalKeysExamined": 0,
"totalDocsExamined": 230045,
"executionStages": {
"stage": "PROJECTION_DEFAULT",
"nReturned": 230045,
"executionTimeMillisEstimate": 331,
"works": 230047,
"advanced": 230045,
"needTime": 1,
"needYield": 0,
"saveState": 254,
"restoreState": 254,
"isEOF": 1,
"transformBy": {
"uniqueId": true,
"shortLabel": true,
"id": true,
"geographicLocation": {
"id": true
},
"_id": false
},
"inputStage": {
"stage": "COLLSCAN",
"nReturned": 230045,
"executionTimeMillisEstimate": 70,
"works": 230047,
"advanced": 230045,
"needTime": 1,
"needYield": 0,
"saveState": 254,
"restoreState": 254,
"isEOF": 1,
"direction": "forward",
"docsExamined": 230045
}
},
"allPlansExecution": []
}
},
"nReturned": 230045,
"executionTimeMillisEstimate": 360
},
{
"$lookup": {
"from": "geographic_location",
"as": "region",
"localField": "geographicLocation.id",
"foreignField": "id"
},
"nReturned": 230045,
"executionTimeMillisEstimate": 11840
},
{
"$match": {
"region.name": {
"$eq": "United States"
}
},
"nReturned": 114639,
"executionTimeMillisEstimate": 12004
},
{
"$facet": {
"metadata": [
{
"$teeConsumer": {},
"nReturned": 114639,
"executionTimeMillisEstimate": 12123
},
{
"$group": {
"_id": {
"$const": null
},
"total": {
"$sum": {
"$const": 1
}
}
},
"nReturned": 1,
"executionTimeMillisEstimate": 12134
},
{
"$project": {
"total": true,
"_id": false
},
"nReturned": 1,
"executionTimeMillisEstimate": 12134
},
{
"$addFields": {
"page": {
"$const": 1
}
},
"nReturned": 1,
"executionTimeMillisEstimate": 12134
}
],
"data": [
{
"$teeConsumer": {},
"nReturned": 10,
"executionTimeMillisEstimate": 0
},
{
"$limit": 10,
"nReturned": 10,
"executionTimeMillisEstimate": 0
}
]
},
"nReturned": 1,
"executionTimeMillisEstimate": 12134
}
],
"serverInfo": {
"host": "",
"port": ,
"version": "4.4.0",
"gitVersion": ""
},
"ok": 1
}
你知道我的问题出在哪里吗?
1条答案
按热度按时间ttp71kqs1#
您正在尝试提高此操作的性能,但您的目标是什么?您是希望将时间缩短10%、满足特定SLA还是其他要求?此操作运行的频率是多少?数据是否会继续增长?
总的来说,这里没有足够的信息来充分回答这个问题。也就是说,我们仍然可以做出一些观察和指示。尽管如此,我怀疑这里可能存在一个架构问题,而且似乎不太可能使用当前的方法来实现您想要的性能。
在较高的层次上,下面是数据库执行此聚合所做的工作:
unique_ids_in_each_bank
集合中的230,045
文档执行完整集合扫描。这是因为您没有以任何方式限制来自原始集合的数据。这就是您在explain
输出中看到对COLLSCAN
的引用的原因。这些文档中的每一个都通过$project
阶段进行转换,然后传递到后续阶段进行进一步处理。230,045
次),在geographic_location
集合中执行$lookup
。对于上面的示例查询,$lookup
操作 * 可以 * 使用{ id: 1, name: 1 }
上的复合索引,但目前还不清楚这是否会以任何有意义的方式改变业绩。在较新版本的数据库中,$lookup
的explain
输出中有改进的度量。$match
阶段之后,大约一半(114,639
)的原始文档仍然属于结果集,并被传递以供进一步处理。$facet
的两个子管道之一正在计数所有传入文档。因此,所有114,639
文档将流入此阶段并在它将结果(计数和前10项)返回给客户端之前进行处理。看起来数据库需要大约12秒来扫描整个集合,在另一个集合中进行230k次查找,然后处理所有这些结果。这是一个需要做的大量工作,而且确实没有索引或其他有意义的东西来改变这一点。
需要考虑的一些潜在事项:
skip
值的增大,这将变慢。geographic_location
信息合并到源集合中的文档中是否很常见?如果是这样,则强制数据库在每次读取数据时生成新文档可能是一个沉重的负担。最后,请记住,没有(唯一)排序的分页可能会导致意外的结果。