gpt4 book ai didi

c# - Mongodb聚合组性能

转载 作者:太空宇宙 更新时间:2023-11-04 00:10:45 25 4
gpt4 key购买 nike

我对 mongo DB 还很陌生,并在我们的一个应用程序中尝试使用它。我们正在尝试实现 CQRS,查询部分我们正在尝试使用 node.js,而命令部分我们正在通过 c# 实现。

我的一个收藏可能包含数百万份文档。我们将有一个 scenarioId 字段,每个场景可以有大约 200 万条记录。

我们的用例是比较这两个场景数据并对场景的每个字段进行一些数学运算。例如,每个场景都可以有一个属性avgMiles,我想计算这个属性的差异,并且用户应该能够过滤这个差异值。由于我的设计是将两个场景数据保留在单个集合中,因此我尝试按场景 ID 进行分组并进一步对其进行投影。

我的文档示例结构如下所示。

{ 
"_id" : ObjectId("5ac05dc58ff6cd3054d5654c"),
"origin" : {
"code" : "0000",
},
"destination" : {
"code" : "0001",
},
"currentOutput" : {
"avgMiles" : 0.15093020854848138,
},
"scenarioId" : NumberInt(0),
"serviceType" : "ECON"
}

当我分组时,我会根据 origin.codedestination.code 以及 serviceType 属性对其进行分组。

我的聚合管道查询如下所示:

  db.servicestats.aggregate([{$match:{$or:[{scenarioId:0}, {scenarioId:1}]}},
{$sort:{'origin.code':1,'destination.code':1,serviceType:1}},
{$group:{
_id:{originCode:'$origin.code',destinationCode:'$destination.code',serviceType:'$serviceType'},
baseScenarioId:{$sum:{$switch: {
branches: [
{
case: { $eq: [ '$scenarioId', 1] },
then: '$scenarioId'
}],
default: 0
}
}},
compareScenarioId:{$sum:{$switch: {
branches: [
{
case: { $eq: [ '$scenarioId', 0] },
then: '$scenarioId'
}],
default: 0
}
}},
baseavgMiles:{$max:{$switch: {
branches: [
{
case: { $eq: [ '$scenarioId', 1] },
then: '$currentOutput.avgMiles'
}],
default: null
}
}},
compareavgMiles:{$sum:{$switch: {
branches: [
{
case: { $eq: [ '$scenarioId', 0] },
then: '$currentOutput.avgMiles'
}],
default: null
}
}}
}
},
{$project:{scenarioId:
{ base:'$baseScenarioId',
compare:'$compareScenarioId'
},
avgMiles:{base:'$baseavgMiles', comapre:'$compareavgMiles',diff:{$subtract :['$baseavgMiles','$compareavgMiles']}}
}
},
{$match:{'avgMiles.diff':{$eq:0.5}}},
{$limit:100}
],{allowDiskUse: true} )

我的小组管道阶段将包含 400 万份文档。您能否建议我如何提高此查询的性能?

我对分组依据条件中使用的字段有一个索引,并且添加了排序管道阶段以帮助分组依据更好地执行。

非常欢迎任何建议。

由于 group by 在我的情况下不起作用,我已经使用 $lookup 实现了左外连接,查询如下所示。

    db.servicestats.aggregate([
{$match:{$and :[ {'scenarioId':0}
//,{'origin.code':'0000'},{'destination.code':'0001'}
]}},
//{$limit:1000000},
{$lookup: { from:'servicestats',
let: {ocode:'$origin.code',dcode:'$destination.code',stype:'$serviceType'},
pipeline:[
{$match: {
$expr: { $and:
[
{ $eq: [ "$scenarioId", 1 ] },
{ $eq: [ "$origin.code", "$$ocode" ] },
{ $eq: [ "$destination.code", "$$dcode" ] },
{ $eq: [ "$serviceType", "$$stype" ] },
]
}

}
},
{$project: {_id:0, comp :{compavgmiles :'$currentOutput.avgMiles'}}},
{ $replaceRoot: { newRoot: "$comp" } }
],
as : "compoutputs"
}},
{
$replaceRoot: {
newRoot: {
$mergeObjects:[
{
$arrayElemAt: [
"$$ROOT.compoutputs",
0
]
},
{
origin: "$$ROOT.origin",
destination: "$$ROOT.destination",
serviceType: "$$ROOT.serviceType",
baseavgmiles: "$$ROOT.currentOutput.avgMiles",
output: '$$ROOT'
}
]
}
}
},
{$limit:100}
])

上述查询性能良好,70毫秒内返回。

但在我的场景中,我需要实现一个完整的外部联接,我知道 mongo 目前还不支持,并使用 $facet 管道实现,如下所示

    db.servicestats.aggregate([
{$limit:1000},
{$facet: {output1:[
{$match:{$and :[ {'scenarioId':0}
]}},
{$lookup: { from:'servicestats',
let: {ocode:'$origin.code',dcode:'$destination.code',stype:'$serviceType'},
pipeline:[
{$match: {
$expr: { $and:
[
{ $eq: [ "$scenarioId", 1 ] },
{ $eq: [ "$origin.code", "$$ocode" ] },
{ $eq: [ "$destination.code", "$$dcode" ] },
{ $eq: [ "$serviceType", "$$stype" ] },
]
}

}
},
{$project: {_id:0, comp :{compavgmiles :'$currentOutput.avgMiles'}}},
{ $replaceRoot: { newRoot: "$comp" } }
],
as : "compoutputs"
}},
//{
// $replaceRoot: {
// newRoot: {
// $mergeObjects:[
// {
// $arrayElemAt: [
// "$$ROOT.compoutputs",
// 0
// ]
// },
// {
// origin: "$$ROOT.origin",
// destination: "$$ROOT.destination",
// serviceType: "$$ROOT.serviceType",
// baseavgmiles: "$$ROOT.currentOutput.avgMiles",
// output: '$$ROOT'
// }
// ]
// }
// }
// }
],
output2:[
{$match:{$and :[ {'scenarioId':1}
]}},
{$lookup: { from:'servicestats',
let: {ocode:'$origin.code',dcode:'$destination.code',stype:'$serviceType'},
pipeline:[
{$match: {
$expr: { $and:
[
{ $eq: [ "$scenarioId", 0 ] },
{ $eq: [ "$origin.code", "$$ocode" ] },
{ $eq: [ "$destination.code", "$$dcode" ] },
{ $eq: [ "$serviceType", "$$stype" ] },
]
}

}
},
{$project: {_id:0, comp :{compavgmiles :'$currentOutput.avgMiles'}}},
{ $replaceRoot: { newRoot: "$comp" } }
],
as : "compoutputs"
}},
//{
// $replaceRoot: {
// newRoot: {
// $mergeObjects:[
// {
// $arrayElemAt: [
// "$$ROOT.compoutputs",
// 0
// ]
// },
// {
// origin: "$$ROOT.origin",
// destination: "$$ROOT.destination",
// serviceType: "$$ROOT.serviceType",
// baseavgmiles: "$$ROOT.currentOutput.avgMiles",
// output: '$$ROOT'
// }
// ]
// }
// }
// },
{$match :{'compoutputs':{$eq:[]}}}

]
}
}




///{$limit:100}
])

但是facet性能非常糟糕。欢迎任何进一步改进的想法。

最佳答案

一般来说,有以下三种情况会导致查询缓慢:

  1. 查询未建立索引,无法有效使用索引,或者架构设计不是最佳的(例如高度嵌套的数组或子文档),这意味着 MongoDB 必须做一些额外的工作才能获得相关数据。
  2. 查询正在等待一些缓慢的事情(例如从磁盘获取数据、将数据写入磁盘)。
  3. 硬件配置不足。

就您的查询而言,可能有一些有关查询性能的一般性建议:

  • 在聚合管道中使用 allowDiskUse 意味着查询可能会在某些阶段使用磁盘。磁盘通常是机器中最慢的部分,因此如果您可以避免这种情况,它将加快查询速度。

  • 请注意,聚合查询仅限使用 100MB 内存。这与您拥有的内存量无关。

  • $group 阶段无法使用索引,因为索引与文档在磁盘上的位置相关联。一旦聚合管道进入与文档的物理位置无关的阶段(例如 $group 阶段),就无法再使用索引。

  • 默认情况下,WiredTiger 缓存约为 RAM 的 50%,因此 64GB 计算机将具有约 32GB WiredTiger 缓存。如果你发现查询非常慢,有可能MongoDB需要到磁盘去获取相关文档。在查询期间监视 iostats 并检查磁盘利用率%将提供是否配置了足够 RAM 的提示。

一些可能的解决方案是:

  • 配置更多 RAM,以便 MongoDB 不必经常访问磁盘。
  • 重新设计架构,以避免文档中存在大量嵌套字段或多个数组。
  • 定制文档架构,以便您更轻松地查询其中的数据,而不是根据您认为的数据存储方式定制架构(例如,避免关系数据库设计模型中固有的严重规范化)。
  • 如果您发现达到了单台计算机的性能限制,请考虑使用分片来水平扩展查询。但请注意,分片是一种需要仔细设计和考虑的解决方案。

关于c# - Mongodb聚合组性能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49685392/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com