【发布时间】:2015-06-04 19:45:35
【问题描述】:
目前我正在针对包含用户和事件信息的集合运行聚合。例如:
[
{
$match: {
client: ObjectId('507f1f77bcf86cd799439011'),
location: 'UK'
}
},
{
$group: {
_id: null,
count: {
$sum: 1
}
}
}
]
上面是一个很大的简化,可以说有大约 20 个不同的变量,比如 location 可以进入 $match 语句。这两者之间有时还会有额外的步骤,这就是我使用$group 进行计数的原因。 (而不是count)
目前我在client 字段上有一个索引,但还没有在其他字段上创建索引(复合或其他)。由于还有很多其他字段,我不能只为所有内容创建索引 - 这太昂贵了。
问题:这在客户拥有少量文档时非常有用,但随着数量的增长,聚合必须扫描越来越多的文档。索引将范围向下聚焦,但这还不够。
想法
创建一个名为p(用于分区)的附加变量,并创建一个复合索引:{ client: 1, p: 1 }。 p 可以是1-n。
不要运行上面的管道,而是运行类似的管道n 次:(对于p 的所有可能值)
[
{
$match: {
client: ObjectId('507f1f77bcf86cd799439011'),
p: 1, // or 2, 3, etc
location: 'UK'
}
},
{
$group: {
_id: null,
count: {
$sum: 1
}
}
}
]
然后可以在应用程序级别合并来自所有管道的结果。
使用这种方法,我可以限制每个查询必须执行的扫描次数,理论上可以减少查询时间。
更进一步,p 值可以用作分片键,因此理论上,分析查询可以跨多个分片并行运行。
以前有人做过这样的事吗?我在这个主题上发现的很少。
【问题讨论】:
-
你说这是一个简化的例子。它的最终目标是给你一个计数吗?如果不是,目标是什么?我不认为运行相同的查询
n次将是一条好路线。调整管道可能会更好。 -
目标可能是获取计数、文档列表或运行进一步的聚合步骤来总结特定字段。可以进入 $match 步骤的变量因用户输入而异,但我无法为所有变量创建索引 - 管道已经调整。考虑到这一点,现在运行 n 次查询是一条好路吗?如果不是,为什么?
-
使用应用程序逻辑来强制执行数据库之外的性能,您创建的方法是不可扩展的。当数据库增长更多时会发生什么?添加更多
ps。最终你会陷入同样的境地。我询问最终目标的原因是也许……聚合不是这项工作的正确工具,也许是。上面的例子可以用 .count(query) 来解决。如果您传递的领域比需要的多,Mongo 会变慢。例如 500k 文档上的 collection.find({},{_id:1}) 将立即返回,因为它不会尝试检索完整文档。 -
同意 - 上面的答案可以用
count解决,也许我应该坚持这一点而不是使用管道(即使这是我们正在使用的)。随着数据库的增长,添加更多ps 正是我想要做的,是的。这显然不会无限期地工作,但我看不到我们很快就会使用超过 20 个ps。考虑到这一点,每个单独的查询都会更快,因此整体“查询”将是,不是吗? -
我个人不这么认为。但是您可以做的是运行测试并在查询结束时抛出
.explain("executionStats"),您将获得性能统计信息。如果查询 mils * 20p加上任何你需要组合的结果是合理的,那么你应该是好的。
标签: mongodb