Time-Series集合不支持TTL索引,必须在createCollection时通过expireAfterSeconds设置过期策略,该参数作用于bucket文档的_id(基于control.max.time),最小值为3600秒,且不可修改。

Time-Series集合不支持直接设置expireAfterSeconds
这是最常被误解的一点:你不能对Time-Series集合本身调用createIndex({ "timeField": 1 }, { expireAfterSeconds: 3600 }),MongoDB会报错Cannot create TTL index on a time-series collection。Time-Series集合的过期机制和普通集合完全不同,它依赖底层的“bucket”结构和自动滚动策略,而非TTL索引。
必须通过expireAfterSeconds选项在创建集合时声明
过期行为只能在db.createCollection()阶段指定,后续无法修改。该参数作用于每个bucket文档的_id(即bucket的时间范围),而非单条时间序列文档。
-
expireAfterSeconds值是相对于bucket结束时间(control.max.time)计算的,不是每条数据的timestamp - 例如设为
3600,表示所有结束时间早于当前时间1小时的bucket会被后台线程清理 - 实际保留时长会有延迟,通常在几分钟到十几分钟内(取决于后台任务调度频率)
- 最小允许值为
3600(1小时),小于该值会报错expireAfterSeconds must be at least 3600
db.createCollection("sensor_data", {
timeseries: {
timeField: "timestamp",
metaField: "metadata",
granularity: "minutes"
},
expireAfterSeconds: 3600
})
granularity影响bucket大小和过期粒度
granularity不是可选参数,它决定了bucket的时间跨度,直接影响过期精度和查询效率:
-
"seconds":bucket最多覆盖1小时数据(默认),适合高精度写入,但bucket数量多、管理开销略高 -
"minutes":bucket最多覆盖24小时,平衡性较好,大多数IoT场景推荐 -
"hours":bucket最多覆盖30天,过期延迟最大,但索引和压缩更高效 - 注意:
expireAfterSeconds始终以bucket为单位清理,哪怕bucket里只有一条1秒前的数据,只要bucket整体“过期”,整个bucket都会被删
验证过期是否生效的三个关键检查点
别只看数据有没有消失——很多问题出在配置未生效或理解偏差上:
- 确认集合确实是Time-Series类型:
db.getCollectionInfos({ name: "sensor_data" })中type字段为"timeseries",且options.expireAfterSeconds存在 - 检查bucket集合是否存在(隐藏):
db.getCollectionNames().filter(n => n.startsWith("system.buckets.")),过期逻辑作用于这些bucket集合,不是原始集合名 - 观察
system.buckets.*集合的文档数量变化,比查原始集合更准确;原始集合的countDocuments()可能因延迟返回过期前的数据
真正容易被忽略的是:过期不是实时的,也不基于单条记录时间戳,而是按bucket切片+后台异步清理。如果你需要毫秒级精确删除,Time-Series集合不适合,得换普通集合加TTL索引。


















