并行运行MongoDB聚合

时间:2015-05-28 19:20:12

标签: mongodb

目前,我正在针对包含用户和事件信息的集合运行聚合。例如:

[
  {
    $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值可用作分片键,因此理论上,分析查询可以跨多个分片并行运行。

以前有人做过这样的事吗?我在这个问题上找不到什么。

1 个答案:

答案 0 :(得分:1)

对此方法的早期测试表明,它非常有效地运行。并行运行多个count查询意味着现在计算“总查询时间”:

total time = max(single query time) + combination time

我还没有大规模地测试过这个,但是在中等规模它是一种绝对的享受。

关于此测试的简要统计数据:

  • 收藏有2.5万份文件
  • 这些文件中的200k具有我关心的client参数
  • 我正在并行运行4个查询,每个查询不同的子集(~50k)文档

对于少量扫描,这种方法几乎没有任何好处。但是,对于上面的示例,我们将total time减少到 2-4x 之间。

看起来这种方法在50-100k子集大小之间有一个最佳点。

当然,并行运行大量查询会使您受到其他MongoDB限制。