在具有数百万条记录转储的复杂查询阶段中,我面临一些查询缓慢的问题。 谁能分享我的一些想法,这可能对我结束这个过程有帮助。
db.getCollection("states").aggregate([
{ "$project": { "_id": 1 } },
{
"$lookup": {
"from": "allbama",
"localField": "_id",
"foreignField": "state",
"pipeline": [
{
"$match": {
"createdAt": {
"$gte": ISODate("2023-01-01T18:30:00.000Z"),
"$lte": ISODate("2024-01-31T18:30:00.000Z")
},
"flag": "Security Flag"
}
},
{ "$project": { "_id": 1 } },
],
"as": "allbama"
}
},
{
"$lookup": {
"from": " alaska",
"localField": "_id",
"foreignField": "state",
"pipeline": [
{
"$match": {
"createdAt": {
"$gte": ISODate("2023-01-01T18:30:00.000Z"),
"$lte": ISODate("2024-01-31T18:30:00.000Z")
},
"flag": "Security Flag"
}
},
{ "$project": { "_id": 1 } }
],
"as": " alaska"
}
},
{
"$lookup": {
"from": " arizona",
"localField": "_id",
"foreignField": "state",
"pipeline": [
{
"$match": {
"createdAt": {
"$gte": ISODate("2023-01-01T18:30:00.000Z"),
"$lte": ISODate("2024-01-31T18:30:00.000Z")
},
"flag": "Security Flag"
}
},
{ "$project": { "_id": 1 } }
],
"as": " arizona"
}
},
{
"$lookup": {
"from": "california",
"localField": "_id",
"foreignField": "state",
"pipeline": [
{
"$match": {
"createdAt": {
"$gte": ISODate("2023-01-01T18:30:00.000Z"),
"$lte": ISODate("2024-01-31T18:30:00.000Z")
},
"flag": "Security Flag"
}
},
{ "$project": { "_id": 1 } }
],
"as": "california"
}
},
{
"$lookup": {
"from": "colorado",
"localField": "_id",
"foreignField": "state",
"pipeline": [
{
"$match": {
"createdAt": {
"$gte": ISODate("2023-01-01T18:30:00.000Z"),
"$lte": ISODate("2024-01-31T18:30:00.000Z")
},
"flag": "Security Flag"
}
},
{ "$project": { "_id": 1 } }
],
"as": "colorado"
}
},
{
"$lookup": {
"from": "florida",
"localField": "_id",
"foreignField": "state",
"pipeline": [
{
"$match": {
"createdAt": {
"$gte": ISODate("2023-01-01T18:30:00.000Z"),
"$lte": ISODate("2024-01-31T18:30:00.000Z")
},
"flag": "Security Flag"
}
},
{ "$project": { "_id": 1 } }
],
"as": "florida"
}
},
{
"$lookup": {
"from": "lowa",
"localField": "_id",
"foreignField": "state",
"pipeline": [
{
"$match": {
"createdAt": {
"$gte": ISODate("2023-01-01T18:30:00.000Z"),
"$lte": ISODate("2024-01-31T18:30:00.000Z")
},
"flag": "Security Flag"
}
},
{ "$project": { "_id": 1 } }
],
"as": "lowa"
}
},
{
"$lookup": {
"from": "maine",
"localField": "_id",
"foreignField": "state",
"pipeline": [
{
"$match": {
"createdAt": {
"$gte": ISODate("2023-01-01T18:30:00.000Z"),
"$lte": ISODate("2024-01-31T18:30:00.000Z")
},
"flag": "Security Flag"
}
},
{ "$project": { "_id": 1 } }
],
"as": "maine"
}
},
{
"$lookup": {
"from": "nevada",
"localField": "_id",
"foreignField": "state",
"pipeline": [
{
"$match": {
"createdAt": {
"$gte": ISODate("2023-01-01T18:30:00.000Z"),
"$lte": ISODate("2024-01-31T18:30:00.000Z")
},
"flag": "Security Flag"
}
},
{ "$project": { "_id": 1 } }
],
"as": "nevada"
}
},
{
"$lookup": {
"from": "oregon",
"localField": "_id",
"foreignField": "state",
"pipeline": [
{
"$match": {
"createdAt": {
"$gte": ISODate("2023-01-01T18:30:00.000Z"),
"$lte": ISODate("2024-01-31T18:30:00.000Z")
},
"flag": "Security Flag"
}
},
{ "$project": { "_id": 1 } }
],
"as": "oregon"
}
},
{
"$lookup": {
"from": "texas",
"localField": "_id",
"foreignField": "state",
"pipeline": [
{
"$match": {
"createdAt": {
"$gte": ISODate("2023-01-01T18:30:00.000Z"),
"$lte": ISODate("2024-01-31T18:30:00.000Z")
},
"flag": "Security Flag"
}
},
{ "$project": { "_id": 1 } }
],
"as": "texas"
}
},
{
"$lookup": {
"from": "utah",
"localField": "_id",
"foreignField": "state",
"pipeline": [
{
"$match": {
"createdAt": {
"$gte": ISODate("2023-01-01T18:30:00.000Z"),
"$lte": ISODate("2024-01-31T18:30:00.000Z")
},
"flag": "Security Flag"
}
},
{ "$project": { "_id": 1 } }
],
"as": "utah"
}
},
{
"$lookup": {
"from": "vermont",
"localField": "_id",
"foreignField": "state",
"pipeline": [
{
"$match": {
"createdAt": {
"$gte": ISODate("2023-01-01T18:30:00.000Z"),
"$lte": ISODate("2024-01-31T18:30:00.000Z")
},
"flag": "Security Flag"
}
},
{ "$project": { "_id": 1 } }
],
"as": "vermont"
}
},
{
"$lookup": {
"from": "virginia",
"localField": "_id",
"foreignField": "state",
"pipeline": [
{
"$match": {
"createdAt": {
"$gte": ISODate("2023-01-01T18:30:00.000Z"),
"$lte": ISODate("2024-01-31T18:30:00.000Z")
},
"flag": "Security Flag"
}
},
{ "$project": { "_id": 1 } }
],
"as": "virginia"
}
},
{
"$lookup": {
"from": "washington",
"localField": "_id",
"foreignField": "state",
"pipeline": [
{
"$match": {
"createdAt": {
"$gte": ISODate("2023-01-01T18:30:00.000Z"),
"$lte": ISODate("2024-01-31T18:30:00.000Z")
},
"flag": "Security Flag"
}
},
{ "$project": { "_id": 1 } }
],
"as": "washington"
}
},
{
"$addFields": {
"hasSecurityFlag": {
"$or": [
{ "$gt": [{ "$size": "$allbama" }, 0] },
{ "$gt": [{ "$size": "$ alaska" }, 0] },
{ "$gt": [{ "$size": "$ arizona" }, 0] },
{ "$gt": [{ "$size": "$california" }, 0] },
{ "$gt": [{ "$size": "$colorado" }, 0] },
{ "$gt": [{ "$size": "$florida" }, 0] },
{ "$gt": [{ "$size": "$lowa" }, 0] },
{ "$gt": [{ "$size": "$maine" }, 0] },
{ "$gt": [{ "$size": "$nevada" }, 0] },
{ "$gt": [{ "$size": "$oregon" }, 0] },
{ "$gt": [{ "$size": "$texas" }, 0] },
{ "$gt": [{ "$size": "$utah" }, 0] },
{ "$gt": [{ "$size": "$vermont" }, 0] },
{ "$gt": [{ "$size": "$virginia" }, 0] },
{ "$gt": [{ "$size": "$washington" }, 0] }
]
}
}
},
{
"$match": {
"hasSecurityFlag": true
}
},
]);
在此查询中,我可以在不使用最终比赛阶段的情况下在 5 秒内从大量记录中获取结果
{"$match": {"hasSecurityFlag": true}}
,但是当我添加此比赛阶段时,需要 3 到 4 分钟才能获得结果。
但这不是默认的收集字段。所以我无法为此比赛阶段添加适当的索引。可以更新哪些内容以使该查询过程优化且高效。
我需要在几秒钟内获得查询结果,并需要解决任何索引问题。
我非常同意@Wernfried Domscheit,即你的模式可能需要重新考虑。对我来说,根本不明白为什么这些信息分布在不同的集合中。也许您可以将所有内容保留在同一个集合中,并向文档添加
state
字段,而不是在集合名称中隐式捕获该信息(并使您的查询变得非常复杂)。
我将尝试回答您的问题,即为什么最后
$match
阶段的存在似乎会极大地影响整体执行时间,并提供一些有关如何按原样改进操作的建议。
$match
速度很慢?简而言之,您的比较不一定像他们所说的“同类比较”。具体来说,当您注意到“能够在不使用最终匹配的情况下在 5 秒内从大量巨大记录中获取结果”时,您还没有澄清这是否是结果的全部,或者只是第一批结果。
我们没有足够的信息可以肯定地说,但这里可能发生的情况是,尾随的
$match
迫使您的聚合阶段比没有它运行时进一步处理。如果没有它,数据库将在准备好后立即反馈第一批结果,而不必丢弃任何结果。但是 with $match
现在必须丢弃不匹配的结果,这会延迟第一批的生成。如果您要在没有 $match
的情况下强制完全执行聚合管道,例如通过 appending .itcount() 或在管道末尾附加 {$count: "total"}
阶段,我怀疑您会看到类似的缓慢情况。
拥有其他信息,例如此源
states
集合中有多少文档以及它们是什么样子,将非常有助于我们更好地理解问题。
我们再次没有足够的信息来确定导致所有缓慢的原因,因此这里的一些建议都是推测性的。
到目前为止,最重要和最明显的事情是确保集合具有适当的索引。因此,对于您调用
$lookup
的每个集合,您需要在 { state: 1, flag: 1, createdAt: 1, _id: 1}
上有一个索引。 _id
部分是可选的,但允许每个操作进行额外的 覆盖优化。
$limit
添加到 $lookup
管道尚不清楚该管道所需的业务逻辑是什么。但是,如果您要查找的“所有”状态在指定时间线内具有一个或多个安全标志,那么您可以向所有
{ $limit: 1 }
中的每个 pipeline
添加 $lookup
阶段。可以写在$project
之前或之后。这将允许数据库在第一个状态被识别后立即停止其工作。
显然,如果您想要州的匹配条目的完整列表,则不能使用此方法。
$unionWith
代替我认为这里没有特别令人信服的理由使用一堆
$lookup
。您可以将所有(除了第一个)的 states
转换为 ,而不是从 $documents
集合中获取所有文档(或类似地使用 $lookup
列出感兴趣的值)。 $unionWith
的阶段。这是否有价值以及这些管道的定义是什么再次取决于您想要实现的特定逻辑。
顺便说一下,你的
allbama
有错别字。