Mongodb 加快大型集合上的聚合
我目前有一个数据库,包含大约27万个文档。它们看起来像这样:Mongodb 加快大型集合上的聚合,mongodb,mongodb-query,aggregation-framework,Mongodb,Mongodb Query,Aggregation Framework,我目前有一个数据库,包含大约27万个文档。它们看起来像这样: [{ 'location': 'Berlin', 'product': 4531, 'createdAt': ISODate(...), 'value': 3523, 'minOffer': 3215, 'quantity': 7812 },{ 'location': 'London', 'product': 1231, 'createdAt': ISODate(
[{
'location': 'Berlin',
'product': 4531,
'createdAt': ISODate(...),
'value': 3523,
'minOffer': 3215,
'quantity': 7812
},{
'location': 'London',
'product': 1231,
'createdAt': ISODate(...),
'value': 53523,
'minOffer': 44215,
'quantity': 2812
}]
该数据库目前拥有一个多月的数据,拥有约170个地点(在欧盟和美国),约8000种产品。这些文档表示时间步,因此每个位置的每个产品每天大约有12-16条条目(但最多每小时1条)。我的目标是检索过去7天内给定位置的产品的所有时间步。对于单个位置,此查询使用索引
{product:1,location:1,createdAt:-1}
以合理的速度运行(150ms)
然而,我也需要这些时间步长,不仅仅是针对单个位置,而是针对整个区域(大约85个位置)。我目前正在使用此聚合进行此操作,它将每小时的所有条目分组,并平均所需的值:
this.db.collection(“…”).aggregate([
{$match:{{location:{$in:[85个位置的数组]},product:productId,createdAt:{$gte:newdate(Date.now()-sevenDaysAgo)}{
$group:{
_身份证:{
$toDate:{
$concat:[
{$toString:{$year:'$createdAt'}},
'-',
{$toString:{$month:'$createdAt'}},
'-',
{$toString:{$dayOfMonth:'$createdAt'}},
' ',
{$toString:{$hour:'$createdAt'}},
':00'
]
}
},
值:{$avg:'$value'},
minOffer:{$avg:'$minOffer'},
数量:{$avg:'$quantity'}
}
}
]).sort({u id:1}).toArray()
但是,即使使用索引{product:1,createdAt:-1,location:1}
(~40秒),这也非常慢。有没有办法加快聚合速度,使其最多只需几秒钟?这可能吗,或者我应该考虑使用其他东西吗?我曾经考虑过将这些聚合保存在另一个数据库中,然后检索并聚合其余的,但是对于网站上第一批用户来说,这真的很尴尬,因为他们不得不等待40秒。这些想法可以提高查询和性能。所有这些能否共同发挥作用,还需要进行一些试验和测试。另外,请注意,更改数据的存储方式和添加新索引意味着应用程序将发生更改,即捕获数据,并且需要仔细验证对相同数据的其他查询(确保它们不会以错误的方式受到影响)
(A)在文档中存储一天的详细信息: 将一天的数据作为子文档数组存储(嵌入)到同一文档中。每个子文档代表一个小时的条目 发件人: 致: 这意味着每个文档大约有10个条目。为条目添加数据将把数据推送到details数组中,而不是像当前应用程序那样添加文档。如果需要小时信息(时间),也可以将其存储为详细信息子文档的一部分;这将完全取决于您的应用程序需要 这种设计的好处是:
- 要维护和查询的文档数量将减少(每 每天的产品(约10个文档)李>
- 在查询中,组阶段将消失。这将只是一个简单的例子
项目阶段。请注意,
支持$project
和$avg
李>$sum
(B)按地区查询: 当前多个位置(或区域)与此查询文件服务器的匹配:
{location:{$in:[85个位置的数组]}
。此筛选器显示:location:location-1,-或-location:location-3,-或-…,location:location-50
。添加一个新字段,region
,将使用一个匹配值进行筛选
按区域的查询将更改为:
{
$match: {
region: regionId,
product: productId,
createdAt: { $gte: new Date(Date.now() - sevenDaysAgo) }
}
}
将提供regionId
变量以与区域字段匹配
请注意,“按位置”和“按区域”查询都将受益于上述两个考虑因素,A和B
(C)索引注意事项: 当前索引:
{product:1,location:1,createdAt:-1}
考虑到新字段区域
,需要更新索引。如果区域字段上没有索引,则带有区域的查询将无法受益。需要第二个索引;适合查询的复合索引。使用区域字段创建索引意味着写操作的额外开销。此外,还需要考虑内存和存储问题
注意事项:
添加索引后,如果查询使用各自的索引,则需要使用explain
验证查询(“按位置”和“按区域”)。这需要一些测试;反复试验的过程
再次,添加新的数据,以不同的格式存储数据,添加新的索引需要考虑这些:
- 仔细测试和验证其他现有查询是否正常运行李>
- 数据捕获需求的变化李>
- 测试新查询并验证新设计是否按预期执行
{product:1,createdAt:-1,location:1}
作为您所述的索引
我不太清楚您的整个产品是如何构建的,但是我认为最好的解决方案是再收集一个只包含上周“相关”文档的集合
然后,您可以轻松地查询该集合,这在Mongo中也很容易使用。
{
location: 'London',
product: 1231,
createdAt: ISODate(...),
details: [ { value: 53523, minOffer: 44215, quantity: 2812 }, ... ]
}
{
$project: { value: { $avg: '$value' }, minOffer: { $avg: '$minOffer' }, quantity: { $avg: '$quantity' } }
}
{
$match: {
region: regionId,
product: productId,
createdAt: { $gte: new Date(Date.now() - sevenDaysAgo) }
}
}