时序集合需设 featurecompatibilityversion 为 "5.0",granularity 必须匹配采集间隔且聚合中 $datetrunc.unit 要与之对齐或为其整数倍,ttl 索引须复合 metafield 和 timefield,metafield 应稳定,且 5.0 不支持分片。

确认 featureCompatibilityVersion 是 5.0
没设对版本,timeseries 选项会被静默忽略,你以为建的是时序集合,实际还是普通集合——查不出压缩效果、聚合变慢、TTL 不按设备清理,全是因为这个。运行 db.adminCommand({ getParameter: 1, featureCompatibilityVersion: 1 }) 看返回值;如果不是 "5.0",必须提前执行 db.adminCommand({ setFeatureCompatibilityVersion: "5.0" })。Atlas 用户不用操心,但自建集群或从 4.4 升上来的务必检查。
创建时指定 granularity 要匹配真实采集间隔
granularity 不是“精度开关”,它决定底层 bucket 的时间跨度,选错直接拉垮压缩率和范围查询性能。常见错误是 IoT 场景下传感器每 30 秒上报一次,却设 granularity: "seconds",结果 MongoDB 强行按秒切片,bucket 内部大量空隙,实测压缩率比设 "minutes" 低 3–5 倍。
- 选
"minutes":适合温湿度、电表、PLC 等 ≥10 秒间隔、常按小时/天聚合的场景 - 选
"seconds":仅当业务真要亚秒级窗口(如高频金融 tick),且能接受更高磁盘开销 - 选
"hours":适用于日志归档类低频写入,或按天做汇总的监控指标
注意:granularity 不影响你插入什么时间值,也不改变 $dateTrunc 的写法,只影响物理存储组织方式。
聚合查询必须用 $dateTrunc 对齐 granularity
哪怕你设了 granularity: "minutes",如果聚合里写 { $dateTrunc: { date: "$ts", unit: "hours" } },MongoDB 就没法利用时序集合的预分桶结构,会退化成全扫描。性能断崖式下跌不是报错,而是悄无声息变慢。
- 必须让
$dateTrunc.unit和创建时的granularity一致,或为其整数倍(如granularity: "minutes",可用unit: "hours",但不能用"seconds") - 按设备+时间聚合时,
$group阶段的_id应包含metaField和$dateTrunc结果,例如:{ _id: { device: "$device", hour: { $dateTrunc: { date: "$ts", unit: "hours" } } } } - 避免在
$match中对timeField做非范围查询(如$eq单点),时序集合对时间范围扫描优化好,对点查不敏感
TTL 索引必须复合 metaField + timeField
只在 timeField 上建 TTL 索引,MongoDB 会扫完整个时间线清理,单次操作压力大、延迟高、还可能卡住写入。正确做法是建复合索引:db.sensors.createIndex({ device: 1, ts: 1 }, { expireAfterSeconds: 2592000 })(30 天)。这样清理按 device 分批推进,IO 更平滑。
-
metaField必须稳定——别把频繁变更的字段(如在线状态、电量)当metaField,否则引发桶分裂,写入放大 - 时序集合不支持事务、不支持更新、不支持手动删除,设计阶段就要确认业务能否接受“只追加+自动过期”模型
- 5.0 不支持分片,6.0 才支持;如果数据量大到单节点扛不住,得规划升 6.0 或前置分库
最易被忽略的是 granularity 与 $dateTrunc 的隐式耦合——它不报错,但一查就慢,一压就崩,问题定位成本远高于创建时多看两眼文档。











