时序集合必须用db.createCollection()显式创建并配置timeseries选项,timeField须为Date类型且非嵌套,metaField若指定则需类型一致;bucketRoundingSeconds与granularity不可混用,其值影响聚合精度;需手动创建复合索引以支持$match和$setWindowFields高效执行。

时序集合必须显式创建,不能靠改名或转换
你不能把一个普通集合改名成 weather 就当它是时序集合——MongoDB 不认。必须用 db.createCollection() 显式声明 timeseries 选项,否则内部不会启用列存、时间聚簇和自动索引优化。
常见错误是先建好普通集合再插入大量数据,后期想“升级”为时序集合,结果发现 $lookup 或 $group 还是慢得离谱。这不是 bug,是设计约束。
- 正确写法:
db.createCollection("metrics", {timeseries: {timeField: "ts", metaField: "tags"}}) -
timeField必须是Date类型字段,且不能是嵌套路径(如"data.ts"不合法) -
metaField可选,但一旦指定,所有文档都必须含该字段,且类型一致(推荐用对象,别用字符串)
granularity 和 bucketRoundingSeconds 必须严格匹配
6.3+ 支持用 bucketRoundingSeconds 替代 granularity,但二者不能混用;若同时出现,MongoDB 会报错 InvalidOptions。更关键的是:如果你设了 bucketRoundingSeconds: 300(5 分钟),那所有写入的 ts 值会被向下取整到最近的 5 分钟边界——这直接影响后续按小时/天聚合的精度和性能。
比如你查 “每小时平均 CPU 使用率”,如果原始数据被塞进 5 分钟桶,MongoDB 就不用再扫描几十万条细粒度文档,直接读几个桶就能算出结果。
- 高频写入 + 低频粗粒度查询 → 用
granularity: "minutes"或bucketRoundingSeconds: 60 - 监控类场景(秒级采集,按天分析)→ 推荐
bucketRoundingSeconds: 3600,并确保应用层写入的ts是标准 UTC 时间 - 千万别设
bucketRoundingSeconds: 1—— 这等于禁用桶优化,失去时序集合核心价值
二次聚合前必须补全 metaField 上的复合索引
时序集合自动创建的索引只覆盖 timeField + metaField 组合,但如果你在聚合里用了 $match 过滤 tags.service: "api",而没对 tags 字段建索引,MongoDB 仍会全桶扫描——尤其当 tags 是嵌套对象时,{ "tags.service": 1 } 这种单字段索引无效。
正确做法是建一个覆盖常用查询路径的复合索引,顺序必须和 $match 条件一致:
- 例如聚合中写
{$match: { "tags.env": "prod", "tags.service": "auth" }},就建db.metrics.createIndex({"tags.env": 1, "tags.service": 1, "ts": -1}) - 注意:这个索引不是时序集合自动建的,必须手动执行
- 索引里带上
ts是为了配合后续$sort或时间范围过滤,避免二次内存排序
$setWindowFields 比 $group + $push 更适合窗口计算
如果你的“二次聚合”本质是“每个设备最近 10 条指标取平均值”,别硬套 $group + $push + $slice。这种写法在时序集合上反而容易触发内存溢出,因为 $group 会先把所有匹配文档拉进内存再分组。
$setWindowFields 是 6.0 引入的专用操作符,它利用底层桶结构做滑动窗口计算,不缓存全量数据:
db.metrics.aggregate([
{$match: {"tags.deviceId": "d-789"}},
{$setWindowFields: {
partitionBy: "$tags.deviceId",
sortBy: {ts: 1},
output: {
avgCpu: {$avg: "$cpu", window: {documents: [-9, 0]}}
}
}}
])
这个管道不会产生中间数组,也不依赖 allowDiskUse,只要桶内数据能被索引快速定位,性能就稳定。
真正容易被忽略的点是:窗口函数要求 partitionBy 和 sortBy 字段必须有对应索引支持,否则降级为内存排序——所以前面建的复合索引,不只是为 $match 服务的。


















