Mongodb 聚合 $group 阶段需要很长时间
Mongodb aggregate $group stage takes a long time
我正在练习如何使用MongoDB聚合,但他们似乎需要很长时间(运行宁时间)。
问题似乎在我使用$group
时发生。所有其他查询 运行 都很好。
我有一些 1.3
百万个虚拟文档 需要执行 两个基本操作:得到一个 count 个 IP 地址和 unique 个 IP 地址。
我的 架构 看起来像这样:
{
"_id":"5da51af103eb566faee6b8b4",
"ip_address":"...",
"country":"CL",
"browser":{
"user_agent":...",
}
}
运行 一个基本的 $group
查询平均需要大约 12
秒,这太慢了。
我做了一些研究,有人建议在 ip_addresses
上创建一个 index。这似乎减慢了它的速度,因为查询现在需要 13-15
秒。
我使用 MongoDB 和 查询 我 运行ning 看起来像这样:
visitorsModel.aggregate([
{
'$group': {
'_id': '$ip_address',
'count': {
'$sum': 1
}
}
}
]).allowDiskUse(true)
.exec(function (err, docs) {
if (err) throw err;
return res.send({
uniqueCount: docs.length
})
})
感谢任何帮助。
编辑:我忘了说,有人说这可能是硬件问题?如果有帮助,我将 运行 在核心 i5、8GB RAM 笔记本电脑上查询。
编辑2:查询计划:
{
"stages" : [
{
"$cursor" : {
"query" : {
},
"fields" : {
"ip_address" : 1,
"_id" : 0
},
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "metrics.visitors",
"indexFilterSet" : false,
"parsedQuery" : {
},
"winningPlan" : {
"stage" : "COLLSCAN",
"direction" : "forward"
},
"rejectedPlans" : [ ]
},
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 1387324,
"executionTimeMillis" : 7671,
"totalKeysExamined" : 0,
"totalDocsExamined" : 1387324,
"executionStages" : {
"stage" : "COLLSCAN",
"nReturned" : 1387324,
"executionTimeMillisEstimate" : 9,
"works" : 1387326,
"advanced" : 1387324,
"needTime" : 1,
"needYield" : 0,
"saveState" : 10930,
"restoreState" : 10930,
"isEOF" : 1,
"invalidates" : 0,
"direction" : "forward",
"docsExamined" : 1387324
}
}
}
},
{
"$group" : {
"_id" : "$ip_address",
"count" : {
"$sum" : {
"$const" : 1
}
}
}
}
],
"ok" : 1
}
您可以创建索引
db.collectionname.createIndex( { ip_address: "text" } )
试试这个,速度更快。
我想它会对你有所帮助。
这是关于使用 $group
聚合阶段的一些信息,如果它使用索引,它的局限性以及可以尝试克服这些的方法。
1. $group 阶段不使用索引:
Mongodb Aggregation: Does $group use index?
2. $group 运算符和内存:
The $group
stage has a limit of 100 megabytes of RAM. By default, if
the stage exceeds this limit, $group
returns an error. To allow for
the handling of large datasets, set the allowDiskUse
option to true.
This flag enables $group operations to write to temporary files.
见MongoDb docs on $group Operator and Memory
3.使用 $group 和 Count:
的示例
集合名为 cities
:
{ "_id" : 1, "city" : "Bangalore", "country" : "India" }
{ "_id" : 2, "city" : "New York", "country" : "United States" }
{ "_id" : 3, "city" : "Canberra", "country" : "Australia" }
{ "_id" : 4, "city" : "Hyderabad", "country" : "India" }
{ "_id" : 5, "city" : "Chicago", "country" : "United States" }
{ "_id" : 6, "city" : "Amritsar", "country" : "India" }
{ "_id" : 7, "city" : "Ankara", "country" : "Turkey" }
{ "_id" : 8, "city" : "Sydney", "country" : "Australia" }
{ "_id" : 9, "city" : "Srinagar", "country" : "India" }
{ "_id" : 10, "city" : "San Francisco", "country" : "United States" }
查询集合按国家统计城市:
db.cities.aggregate( [
{ $group: { _id: "$country", cityCount: { $sum: 1 } } },
{ $project: { country: "$_id", _id: 0, cityCount: 1 } }
] )
结果:
{ "cityCount" : 3, "country" : "United States" }
{ "cityCount" : 1, "country" : "Turkey" }
{ "cityCount" : 2, "country" : "Australia" }
{ "cityCount" : 4, "country" : "India" }
4.使用 allowDiskUse 选项:
db.cities.aggregate( [
{ $group: { _id: "$country", cityCount: { $sum: 1 } } },
{ $project: { country: "$_id", _id: 0, cityCount: 1 } }
], { allowDiskUse : true } )
请注意,在这种情况下,它对查询性能或输出没有影响。这只是为了展示用法。
5。尝试的一些选项(建议):
您可以尝试一些事情来获得一些结果(仅供试用):
- 使用
$limit
阶段并限制处理的文档数量和
看看结果如何。例如,您可以尝试 { $limit: 1000 }
。
请注意,此阶段需要在 $group
阶段之前进行。
- 您还可以使用
$group
之前的 $match
、$project
阶段
阶段来控制输入的 shape 和 size。这可能
return 结果(而不是错误)。
[编辑添加]
区分和计数注意事项:
使用相同的 cities
集合 - 要获得独特的国家和它们的数量,您可以尝试使用聚合阶段 $count
和 $group
,如以下两个查询。
不同:
db.cities.aggregate( [
{ $match: { country: { $exists: true } } },
{ $group: { _id: "$country" } },
{ $project: { country: "$_id", _id: 0 } }
] )
结果:
{ "country" : "United States" }
{ "country" : "Turkey" }
{ "country" : "India" }
{ "country" : "Australia" }
要将上述结果作为具有唯一值数组的单个文档来获取,请使用 $addToSet
运算符:
db.cities.aggregate( [
{ $match: { country: { $exists: true } } },
{ $group: { _id: null, uniqueCountries: { $addToSet: "$country" } } },
{ $project: { _id: 0 } },
] )
结果:{ "uniqueCountries" : [ "United States", "Turkey", "India", "Australia" ] }
计数:
db.cities.aggregate( [
{ $match: { country: { $exists: true } } },
{ $group: { _id: "$country" } },
{ $project: { country: "$_id", _id: 0 } },
{ $count: "uniqueCountryCount" }
] )
结果:{ "uniqueCountryCount" : 4 }
在上面的查询中,$match
阶段用于过滤任何具有不存在或空 country
字段的文档。 $project
阶段重塑结果文档。
MongoDB 查询语言:
请注意,使用 MongoDB 查询语言 命令时,这两个查询会得到相似的结果:db.collection.distinct("country")
和 db.cities.distinct("country").length
(请注意 distinct
return一个数组)。
我正在练习如何使用MongoDB聚合,但他们似乎需要很长时间(运行宁时间)。
问题似乎在我使用$group
时发生。所有其他查询 运行 都很好。
我有一些 1.3
百万个虚拟文档 需要执行 两个基本操作:得到一个 count 个 IP 地址和 unique 个 IP 地址。
我的 架构 看起来像这样:
{
"_id":"5da51af103eb566faee6b8b4",
"ip_address":"...",
"country":"CL",
"browser":{
"user_agent":...",
}
}
运行 一个基本的 $group
查询平均需要大约 12
秒,这太慢了。
我做了一些研究,有人建议在 ip_addresses
上创建一个 index。这似乎减慢了它的速度,因为查询现在需要 13-15
秒。
我使用 MongoDB 和 查询 我 运行ning 看起来像这样:
visitorsModel.aggregate([
{
'$group': {
'_id': '$ip_address',
'count': {
'$sum': 1
}
}
}
]).allowDiskUse(true)
.exec(function (err, docs) {
if (err) throw err;
return res.send({
uniqueCount: docs.length
})
})
感谢任何帮助。
编辑:我忘了说,有人说这可能是硬件问题?如果有帮助,我将 运行 在核心 i5、8GB RAM 笔记本电脑上查询。
编辑2:查询计划:
{
"stages" : [
{
"$cursor" : {
"query" : {
},
"fields" : {
"ip_address" : 1,
"_id" : 0
},
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "metrics.visitors",
"indexFilterSet" : false,
"parsedQuery" : {
},
"winningPlan" : {
"stage" : "COLLSCAN",
"direction" : "forward"
},
"rejectedPlans" : [ ]
},
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 1387324,
"executionTimeMillis" : 7671,
"totalKeysExamined" : 0,
"totalDocsExamined" : 1387324,
"executionStages" : {
"stage" : "COLLSCAN",
"nReturned" : 1387324,
"executionTimeMillisEstimate" : 9,
"works" : 1387326,
"advanced" : 1387324,
"needTime" : 1,
"needYield" : 0,
"saveState" : 10930,
"restoreState" : 10930,
"isEOF" : 1,
"invalidates" : 0,
"direction" : "forward",
"docsExamined" : 1387324
}
}
}
},
{
"$group" : {
"_id" : "$ip_address",
"count" : {
"$sum" : {
"$const" : 1
}
}
}
}
],
"ok" : 1
}
您可以创建索引
db.collectionname.createIndex( { ip_address: "text" } )
试试这个,速度更快。 我想它会对你有所帮助。
这是关于使用 $group
聚合阶段的一些信息,如果它使用索引,它的局限性以及可以尝试克服这些的方法。
1. $group 阶段不使用索引: Mongodb Aggregation: Does $group use index?
2. $group 运算符和内存:
The
$group
stage has a limit of 100 megabytes of RAM. By default, if the stage exceeds this limit,$group
returns an error. To allow for the handling of large datasets, set theallowDiskUse
option to true. This flag enables $group operations to write to temporary files.
见MongoDb docs on $group Operator and Memory
3.使用 $group 和 Count:
集合名为 cities
:
{ "_id" : 1, "city" : "Bangalore", "country" : "India" }
{ "_id" : 2, "city" : "New York", "country" : "United States" }
{ "_id" : 3, "city" : "Canberra", "country" : "Australia" }
{ "_id" : 4, "city" : "Hyderabad", "country" : "India" }
{ "_id" : 5, "city" : "Chicago", "country" : "United States" }
{ "_id" : 6, "city" : "Amritsar", "country" : "India" }
{ "_id" : 7, "city" : "Ankara", "country" : "Turkey" }
{ "_id" : 8, "city" : "Sydney", "country" : "Australia" }
{ "_id" : 9, "city" : "Srinagar", "country" : "India" }
{ "_id" : 10, "city" : "San Francisco", "country" : "United States" }
查询集合按国家统计城市:
db.cities.aggregate( [
{ $group: { _id: "$country", cityCount: { $sum: 1 } } },
{ $project: { country: "$_id", _id: 0, cityCount: 1 } }
] )
结果:
{ "cityCount" : 3, "country" : "United States" }
{ "cityCount" : 1, "country" : "Turkey" }
{ "cityCount" : 2, "country" : "Australia" }
{ "cityCount" : 4, "country" : "India" }
4.使用 allowDiskUse 选项:
db.cities.aggregate( [
{ $group: { _id: "$country", cityCount: { $sum: 1 } } },
{ $project: { country: "$_id", _id: 0, cityCount: 1 } }
], { allowDiskUse : true } )
请注意,在这种情况下,它对查询性能或输出没有影响。这只是为了展示用法。
5。尝试的一些选项(建议):
您可以尝试一些事情来获得一些结果(仅供试用):
- 使用
$limit
阶段并限制处理的文档数量和 看看结果如何。例如,您可以尝试{ $limit: 1000 }
。 请注意,此阶段需要在$group
阶段之前进行。 - 您还可以使用
$group
之前的$match
、$project
阶段 阶段来控制输入的 shape 和 size。这可能 return 结果(而不是错误)。
[编辑添加]
区分和计数注意事项:
使用相同的 cities
集合 - 要获得独特的国家和它们的数量,您可以尝试使用聚合阶段 $count
和 $group
,如以下两个查询。
不同:
db.cities.aggregate( [
{ $match: { country: { $exists: true } } },
{ $group: { _id: "$country" } },
{ $project: { country: "$_id", _id: 0 } }
] )
结果:
{ "country" : "United States" }
{ "country" : "Turkey" }
{ "country" : "India" }
{ "country" : "Australia" }
要将上述结果作为具有唯一值数组的单个文档来获取,请使用 $addToSet
运算符:
db.cities.aggregate( [
{ $match: { country: { $exists: true } } },
{ $group: { _id: null, uniqueCountries: { $addToSet: "$country" } } },
{ $project: { _id: 0 } },
] )
结果:{ "uniqueCountries" : [ "United States", "Turkey", "India", "Australia" ] }
计数:
db.cities.aggregate( [
{ $match: { country: { $exists: true } } },
{ $group: { _id: "$country" } },
{ $project: { country: "$_id", _id: 0 } },
{ $count: "uniqueCountryCount" }
] )
结果:{ "uniqueCountryCount" : 4 }
在上面的查询中,$match
阶段用于过滤任何具有不存在或空 country
字段的文档。 $project
阶段重塑结果文档。
MongoDB 查询语言:
请注意,使用 MongoDB 查询语言 命令时,这两个查询会得到相似的结果:db.collection.distinct("country")
和 db.cities.distinct("country").length
(请注意 distinct
return一个数组)。