我一直在解决为什么我创建的MongoDB视图这么慢的问题。该视图以transactions
集合为目标,并返回记录openBalance
大于0
的记录。我还运行了一些附加的聚合阶段,以按所需方式对数据进行整形。
为了加快视图的执行速度,它通过在视图的聚合管道的第一阶段匹配索引字段来利用目标集合上的索引,如下所示:
// View Stage 1
{ "transactions.details.openBalance" : { "$exists" : true, "$gt" : 0.0 } }
经过大量研究,我确定视图中的聚合非常快地返回了数据。缓慢的是作为端点一部分运行的计数:
let count = await db.collection('view_transactions_report').find().count();
所以我现在想弄清楚的是为什么视图上的计数比基础集合上的计数慢得多的原因,以及如何加快计数的速度。或者,也许还有另一种方法来生成计数?
基础集合有大约80万条记录,但是计数很快返回。但是视图的计数返回的速度要慢得多,该视图仅返回最初的800,000条记录中的10,000个过滤后的集合。在细节方面,我说的是集合返回的计数的3/4秒,而mongo视图返回的计数则是6秒。
因此,首先,为什么视图的计数(数据集要小得多)要比基础集合慢得多,其次,我该如何处理视图的计数速度?
我正在运行其他两个聚合查询,以确定totalCustomers
和totalOpenBalance
,它们似乎也运行缓慢(请参见下面的代码)。
我的端点功能代码的相关部分如下:
// previous code
let count = await db.collection('view_transaction_report').find(search).count();
let totalCustomers = await db.collection('view_transaction_report').find(search).count({
$sum: "customer._id"
});
let result = {};
if (totalCustomers > 0) {
result = await db.collection('view_transaction_report').aggregate([{
$match: search,
},
{
$group: {
_id: null,
totalOpenBalance: {
$sum: '$lastTransaction.details.openBalance'
}
}
}
]).next();
}
db.collection('view_transaction_report').find(search).skip(skip).limit(pagesize).forEach(function (doc) {
docs.push(doc);
}, function (err) {
if (err) {
if (!ioOnly) {
return next(err);
} else {
return res(err);
}
}
if (ioOnly) {
res({
sessionId: sessID,
count: count,
data: docs,
totalCustomers: totalCustomers,
totalOpenBalance: result.totalOpenBalance
});
} else {
res.send({
count: count,
data: docs,
totalCustomers: totalCustomers,
totalOpenBalance: result.totalOpenBalance
});
}
});
就executionStats
而言,这是所生成视图的queryPlanner
部分所显示的内容:
"queryPlanner" : {
"plannerVersion" : 1.0,
"namespace" : "vio.transactions",
"indexFilterSet" : false,
"parsedQuery" : {
"$and" : [
{
"transactions.details.openBalance" : {
"$gt" : 0.0
}
},
{
"transactions.destails.openBalance" : {
"$exists" : true
}
}
]
},
"winningPlan" : {
"stage" : "CACHED_PLAN",
"inputStage" : {
"stage" : "FETCH",
"filter" : {
"transactions.details.openBalance" : {
"$exists" : true
}
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"transactions.details.openBalance" : 1.0
},
"indexName" : "openBalance",
"isMultiKey" : true,
"multiKeyPaths" : {
"transactions.details.openBalance" : [
"transactions",
"transactions.details"
]
},
"isUnique" : false,
"isSparse" : true,
"isPartial" : false,
"indexVersion" : 2.0,
"direction" : "forward",
"indexBounds" : {
"transactions.details.openBalance" : [
"(0.0, inf.0]"
]
}
}
}
},
"rejectedPlans" : [
]
}
答案 0 :(得分:0)
在评论中,@ Wan Bachtiar提到“ openBalance”似乎是multikey index。为了澄清,是的,在目标集合中,“ openBalance”字段是数组内的嵌入式字段。即使在视图中也是如此,即使数据以如下方式成形:“ openBalance”是不在数组内的嵌入式字段。
问题所在在于目标集合上的多键索引,因为Mongo不需要遍历与“ openBalance”字段相关的每个数组元素,这从逻辑上讲会大大增加扫描量时间-因为有时有很多与此特定字段有关的数组元素。
经过进一步检查,我意识到我可以通过更改通过ETL将“ openBalance”填充到我们的mongo集合中的方式来解决此问题。通过进行此更改,我将能够使“ openBalance”成为标准索引,而不是多键索引,从而使mongo可以搜索更小的数据集以返回计数。