Robomongo:超出 $group 的内存限制
Robomongo : Exceeded memory limit for $group
我正在使用脚本删除 mongo 上的重复项,它在一个包含 10 个项目的集合中工作,我将其用作测试,但是当我用于包含 600 万个文档的真实集合时,我得到一个错误。
这是我在 Robomongo(现在称为 Robo 3T)中 运行 的脚本:
var bulk = db.getCollection('RAW_COLLECTION').initializeOrderedBulkOp();
var count = 0;
db.getCollection('RAW_COLLECTION').aggregate([
// Group on unique value storing _id values to array and count
{ "$group": {
"_id": { RegisterNumber: "$RegisterNumber", Region: "$Region" },
"ids": { "$push": "$_id" },
"count": { "$sum": 1 }
}},
// Only return things that matched more than once. i.e a duplicate
{ "$match": { "count": { "$gt": 1 } } }
]).forEach(function(doc) {
var keep = doc.ids.shift(); // takes the first _id from the array
bulk.find({ "_id": { "$in": doc.ids }}).remove(); // remove all remaining _id matches
count++;
if ( count % 500 == 0 ) { // only actually write per 500 operations
bulk.execute();
bulk = db.getCollection('RAW_COLLECTION').initializeOrderedBulkOp(); // re-init after execute
}
});
// Clear any queued operations
if ( count % 500 != 0 )
bulk.execute();
这是错误信息:
Error: command failed: {
"errmsg" : "exception: Exceeded memory limit for $group, but didn't allow external sort. Pass allowDiskUse:true to opt in.",
"code" : 16945,
"ok" : 0
} : aggregate failed :
_getErrorWithCode@src/mongo/shell/utils.js:23:13
doassert@src/mongo/shell/assert.js:13:14
assert.commandWorked@src/mongo/shell/assert.js:266:5
DBCollection.prototype.aggregate@src/mongo/shell/collection.js:1215:5
@(shell):1:1
所以我需要设置 allowDiskUse:true
才能工作?我在脚本中的什么位置执行此操作,执行此操作有什么问题吗?
{ allowDiskUse: true }
应该放在聚合管道之后。
在你的代码中应该是这样的:
db.getCollection('RAW_COLLECTION').aggregate([
// Group on unique value storing _id values to array and count
{ "$group": {
"_id": { RegisterNumber: "$RegisterNumber", Region: "$Region" },
"ids": { "$push": "$_id" },
"count": { "$sum": 1 }
}},
// Only return things that matched more than once. i.e a duplicate
{ "$match": { "count": { "$gt": 1 } } }
], { allowDiskUse: true } )
注意:使用 { allowDiskUse: true }
可能会引入与性能相关的问题,因为聚合管道将从磁盘上的临时文件访问数据。还取决于磁盘性能和工作集的大小。针对您的用例测试性能
The $group stage has a limit of 100 megabytes of RAM. By default, if
the stage exceeds this limit, $group will produce an error. However,
to allow for the handling of large datasets, set the allowDiskUse
option to true to enable $group operations to write to temporary
files. See db.collection.aggregate() method and the aggregate command
for details.
当你有大数据时,最好在组之前使用匹配。
如果你在分组前使用匹配,你就不会遇到这个问题。
db.getCollection('sample').aggregate([
{$match:{State:'TAMIL NADU'}},
{$group:{
_id:{DiseCode:"$code", State:"$State"},
totalCount:{$sum:1}
}},
{
$project:{
Code:"$_id.code",
totalCount:"$totalCount",
_id:0
}
}
])
如果你真的在没有匹配的情况下解决了这个问题,那么解决方案是{ allowDiskUse: true }
这是一个简单的未记录的技巧,可以在很多情况下帮助避免磁盘使用。
您可以使用中间 $project
阶段来减少在 $sort
阶段传递的记录的大小。
在这个例子中,它将驱动到:
var bulk = db.getCollection('RAW_COLLECTION').initializeOrderedBulkOp();
var count = 0;
db.getCollection('RAW_COLLECTION').aggregate([
// here is the important stage
{ "$project": { "_id": 1, "RegisterNumber": 1, "Region": 1 } }, // this will reduce the records size
{ "$group": {
"_id": { RegisterNumber: "$RegisterNumber", Region: "$Region" },
"ids": { "$push": "$_id" },
"count": { "$sum": 1 }
}},
{ "$match": { "count": { "$gt": 1 } } }
]).forEach(function(doc) {
var keep = doc.ids.shift(); // takes the first _id from the array
bulk.find({ "_id": { "$in": doc.ids }}).remove(); // remove all remaining _id matches
count++;
if ( count % 500 == 0 ) { // only actually write per 500 operations
bulk.execute();
bulk = db.getCollection('RAW_COLLECTION').initializeOrderedBulkOp(); // re-init after execute
}
});
看到这里的第一个 $project
阶段只是为了避免磁盘使用。
这对于收集大型记录特别有用,其中大部分数据未在聚合中使用
我正在使用脚本删除 mongo 上的重复项,它在一个包含 10 个项目的集合中工作,我将其用作测试,但是当我用于包含 600 万个文档的真实集合时,我得到一个错误。
这是我在 Robomongo(现在称为 Robo 3T)中 运行 的脚本:
var bulk = db.getCollection('RAW_COLLECTION').initializeOrderedBulkOp();
var count = 0;
db.getCollection('RAW_COLLECTION').aggregate([
// Group on unique value storing _id values to array and count
{ "$group": {
"_id": { RegisterNumber: "$RegisterNumber", Region: "$Region" },
"ids": { "$push": "$_id" },
"count": { "$sum": 1 }
}},
// Only return things that matched more than once. i.e a duplicate
{ "$match": { "count": { "$gt": 1 } } }
]).forEach(function(doc) {
var keep = doc.ids.shift(); // takes the first _id from the array
bulk.find({ "_id": { "$in": doc.ids }}).remove(); // remove all remaining _id matches
count++;
if ( count % 500 == 0 ) { // only actually write per 500 operations
bulk.execute();
bulk = db.getCollection('RAW_COLLECTION').initializeOrderedBulkOp(); // re-init after execute
}
});
// Clear any queued operations
if ( count % 500 != 0 )
bulk.execute();
这是错误信息:
Error: command failed: {
"errmsg" : "exception: Exceeded memory limit for $group, but didn't allow external sort. Pass allowDiskUse:true to opt in.",
"code" : 16945,
"ok" : 0
} : aggregate failed :
_getErrorWithCode@src/mongo/shell/utils.js:23:13
doassert@src/mongo/shell/assert.js:13:14
assert.commandWorked@src/mongo/shell/assert.js:266:5
DBCollection.prototype.aggregate@src/mongo/shell/collection.js:1215:5
@(shell):1:1
所以我需要设置 allowDiskUse:true
才能工作?我在脚本中的什么位置执行此操作,执行此操作有什么问题吗?
{ allowDiskUse: true }
应该放在聚合管道之后。
在你的代码中应该是这样的:
db.getCollection('RAW_COLLECTION').aggregate([
// Group on unique value storing _id values to array and count
{ "$group": {
"_id": { RegisterNumber: "$RegisterNumber", Region: "$Region" },
"ids": { "$push": "$_id" },
"count": { "$sum": 1 }
}},
// Only return things that matched more than once. i.e a duplicate
{ "$match": { "count": { "$gt": 1 } } }
], { allowDiskUse: true } )
注意:使用 { allowDiskUse: true }
可能会引入与性能相关的问题,因为聚合管道将从磁盘上的临时文件访问数据。还取决于磁盘性能和工作集的大小。针对您的用例测试性能
The $group stage has a limit of 100 megabytes of RAM. By default, if the stage exceeds this limit, $group will produce an error. However, to allow for the handling of large datasets, set the allowDiskUse option to true to enable $group operations to write to temporary files. See db.collection.aggregate() method and the aggregate command for details.
当你有大数据时,最好在组之前使用匹配。 如果你在分组前使用匹配,你就不会遇到这个问题。
db.getCollection('sample').aggregate([
{$match:{State:'TAMIL NADU'}},
{$group:{
_id:{DiseCode:"$code", State:"$State"},
totalCount:{$sum:1}
}},
{
$project:{
Code:"$_id.code",
totalCount:"$totalCount",
_id:0
}
}
])
如果你真的在没有匹配的情况下解决了这个问题,那么解决方案是{ allowDiskUse: true }
这是一个简单的未记录的技巧,可以在很多情况下帮助避免磁盘使用。
您可以使用中间 $project
阶段来减少在 $sort
阶段传递的记录的大小。
在这个例子中,它将驱动到:
var bulk = db.getCollection('RAW_COLLECTION').initializeOrderedBulkOp();
var count = 0;
db.getCollection('RAW_COLLECTION').aggregate([
// here is the important stage
{ "$project": { "_id": 1, "RegisterNumber": 1, "Region": 1 } }, // this will reduce the records size
{ "$group": {
"_id": { RegisterNumber: "$RegisterNumber", Region: "$Region" },
"ids": { "$push": "$_id" },
"count": { "$sum": 1 }
}},
{ "$match": { "count": { "$gt": 1 } } }
]).forEach(function(doc) {
var keep = doc.ids.shift(); // takes the first _id from the array
bulk.find({ "_id": { "$in": doc.ids }}).remove(); // remove all remaining _id matches
count++;
if ( count % 500 == 0 ) { // only actually write per 500 operations
bulk.execute();
bulk = db.getCollection('RAW_COLLECTION').initializeOrderedBulkOp(); // re-init after execute
}
});
看到这里的第一个 $project
阶段只是为了避免磁盘使用。
这对于收集大型记录特别有用,其中大部分数据未在聚合中使用