我有一个很大的集合,正在其上运行聚合管道。我有 MongoDB 2.4。我即将达到 16 MB 的限制。我知道我可以通过升级到 2.6 来解决这个问题,但这对我来说不是一个选择。
我可以低于 16 MB 限制的另一种方法是将聚合分解为几个部分,然后将部分结果合并到我的应用程序代码中。我需要拆分的字段是 ObjectId。本质上,我想要的是我的 $match 阶段使用类似的东西:
my_objid_field: {$mod: [10, n]}
我将针对不同的 n 值运行查询 10 次。但是,我不知道如何表达。
<小时/>典型的文档如下所示:
{
"_id" : ObjectId("514cf080358a7c3fd4113f84"),
"a" : 1,
"c" : "US",
"d" : ISODate("2013-03-23T00:00:00Z"),
"st" : ObjectId("4fcfa494c212e76b890004a2"),
"si" : 0,
"so" : ObjectId("4e9e58e62b28686b47e71cdf"),
"t" : ISODate("2013-03-23T00:00:00.779Z"),
"u" : ObjectId("4fe9845a8596aa3d990014cf"),
"se" : "dYJgW8w/kcCIJK08"
}
来自 db.currentOp() 的管道是:
"pipeline" : [
{
"$match" : {
"$or" : [
{
"du" : {
"$gt" : 25
}
},
{
"du" : {
"$exists" : false
}
}
],
"bu" : {
"$exists" : false
},
"t" : {
"$gte" : ISODate("2013-03-23T00:00:00Z"),
"$lt" : ISODate("2013-03-24T00:00:00Z")
}
}
},
{
"$group" : {
"c" : {
"$sum" : 1
},
"_id" : {
"t" : "$st",
"o" : "$so"
}
}
}
]
该查询匹配大约 2000 万个文档,并生成大约 20 万个文档。查询运行几分钟,然后失败并显示“聚合结果超出最大文档大小 (16MB)”。
最佳答案
您的结果太大,因此最好的办法可能是实现 $limit
在管道的末尾:
db.collection.aggregate([
// same $match
// same $group
{ "$sort": { "_id": 1 } },
{ "$limit": 1000 } // or whatever you can go to without breaking
])
问题是 $sort
因为您聚合的结果不能保证按顺序排列,并且可能按发现的顺序排列。您需要这些结果才能进行下一步操作。
在下一次调用时,您将采用“最后一个” _id
聚合的值(value)并更改您的匹配管道,如下所示:
db.collection.aggregate([
{ "$match" : {
"st": { "$gte": ObjectId("4fcfa494c212e76b890004a2") }, // part of last result
"$or" : [
{ "du" : { "$gt" : 25 } },
{ "du" : { "$exists" : false } }
],
"bu" : { "$exists" : false },
"t" : {
"$gte" : ISODate("2013-03-23T00:00:00Z"),
"$lt" : ISODate("2013-03-24T00:00:00Z")
}
}},
{ "$group": {
"_id": { "t" : "$st", "o" : "$so" },
"c" : { "$sum" : 1 },
}},
{ "$match": {
"_id": { // Both elements of the last seen _id
"$ne": {
"t": ObjectId("4fcfa494c212e76b890004a2"),
"o": ObjectId("4e9e58e62b28686b47e71cdf")
}
}
}},
{ "$sort": { "_id": 1 } },
{ "$limit": 1000 }
])
由于您在聚合结果中使用“st”,并且提供的值是最后一组结果中看到的最后一个值,因此所有小于该值的值都将被排除。
决赛$match
是否存在,因为虽然第一个大部分会排除结果,但需要排除“组合键”。这本质上就是为什么你不能只做 $gt
在第一$match
因为组合中共享第一个元素的第二个元素仍然可能有更大的值。
你还是$sort
和$limit
每次迭代并继续,直到返回结果的数量小于您设置的限制。
还有 $skip
聚合管道的运算符,但这不是很高效,因为您将增加每 1000 个文档的“跳过”次数,直到处理 200,000 个结果。所以非常慢。
最好的方法是排除已经看到的值,然后一直切断管道结果。
这里的主要问题是 _id 组合,它本质上是结果。仅仅找到两者组合范围的“分割”将变得非常困难。因此,这里的妥协是每次迭代都会变得更快的查询。
最后,出于性能原因,在复合索引中包含“st”字段现在变得很重要,因为它可以在 $match
中使用。因为它是最有效的形式。
应该尽快考虑迁移到 MongoDB 2.6。
关于mongodb - 通过 mod(ObjectId) 选择文档的一部分,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25193805/