如何让MongoDB高效统计桶中数百万个文档

问题描述 投票:0回答:1

我在 MongoDB 7 中有一个集合,其中包含数百万个文档,如下所示:

{
  _id: ...,
  event_type: "view",
  programId: "12345",
  timestamp: ISODate("2024-01-01T00:00:00Z")
}

timestamp
programId
字段上有索引。

我想创建一个聚合管道,计算给定

programId
每天(或周、月、季度等)的事件数量。这看起来是一个简单的问题,但我不知道如何让 MongoDB 以有效的方式完成它。

我尝试过以下方法:

  • a
    $group
    阶段,使用
    $dateTrunc
    字段上的
    timestamp
    作为
    _id
  • 一个
    $bucket
    阶段,每天/每周/等都有预定义的存储桶
  • 一个
    $facet
    阶段,每个所需的聚合桶都有一个
    [$match, $count]
    管道
  • 以上所有内容,无论是否有索引
    $match
    阶段在它们之前

这些方法都没有达到我想要的性能。其中一些使用

IXSCAN
,而另一些最终使用
COLLSCAN
,但它们都需要一秒钟以上的时间来执行(令人惊讶的是,使用索引时它们通常比不使用索引时慢)。

如果我尝试使用简单的

[$match, $count]
管道查找单个存储桶的计数,MongoDB 将使用
COUNT_SCAN
并在 2 毫秒内返回。有没有办法在聚合多个(可能是数十个或数百个)存储桶时获得这种性能?

我意识到“按需物化视图”或类似的方法可以解决我的问题,但考虑到我想要的信息在索引中很容易获得,如果可以通过简单的管道来完成,我宁愿不引入这种复杂性。

mongodb aggregation-framework
1个回答
0
投票
$dateTrunc

。如果您能够更改架构,则可以预先计算要对其进行计数的值。如果您只想按天获取programId 的计数,则可以将

$dateTrunc
的结果存储在字段
day
中。如果您想更灵活一点,可以使用
$dateToParts
 将时间戳拆分为具有年、月、日、小时等属性的对象。
我在我的机器上使用 1000 万个文档运行测试,其架构类似于此示例:

{ "_id": 1, "programId": "93544", "timestamp": { "$date": "2024-01-23T15:06:00.000Z" }, "parts": { "year": 2024, "month": 1, "day": 23 } }

在程序 id 和零件上创建索引 
programId_1_parts.year_1_parts.month_1_parts.day_1

后,我运行了以下聚合,性能非常好:

[
  {
    $match: {
      programId: "11596",
    },
  },
  {
    $group: {
      _id: {
        year: "$parts.year",
        month: "$parts.month",
        day: "$parts.day",
      },
      cnt: { $sum: 1 },
    },
  },
]

原因是查询已被索引覆盖,因此可以从内存中提供服务。

您的里程可能会有所不同,但我希望这有助于加快速度。

© www.soinside.com 2019 - 2024. All rights reserved.