Time Series集合仅在timeField上自动创建内部聚集索引,不显示在listIndexes()中;device、sensorId等metaField必须手动建索引,否则高频查询将全表扫描。

不能在 timeField 上重复建索引,也不能对 metaField 单独建稀疏索引;所有高频查询字段(尤其是 device、sensorId)必须显式调用 createIndex(),否则默认全表扫描。
为什么 listIndexes() 看不到 timeField 的索引
Time Series 集合底层由隐藏的 bucket 集合支撑,timeField 上的聚集索引是内部管理的,不暴露给用户接口。它不参与 listIndexes() 输出,也不支持你手动重建(执行 createIndex({ ts: 1 }) 会报错 "cannot create index on time-series collection")。这个索引只服务于时间范围查询(如 { ts: { $gt: ISODate("...") } })和列式压缩,不加速其他字段的过滤。
哪些字段必须手动建二级索引
以下字段若出现在高频 $match、$group 前置条件或排序中,必须手动建索引:
-
metaField字段(如device、sensorId):按设备查最新 N 条、或聚合前先过滤设备,没索引就扫全部 bucket - 复合查询场景(如“每个设备最新一条”):建
{ device: 1, ts: -1 },注意顺序——metaField在前、timeField在后,符合 ESR(Equal → Sort → Range)原则 - 测量值字段(如
temperature):仅当高频做范围筛选({ temperature: { $gt: 30 } })且结果集小才考虑;否则压缩后解压开销可能抵消索引收益
granularity 和 TTL 索引的隐含影响
granularity 不改变索引结构,但影响 bucket 内数据密度,间接决定索引效率:
- 设为
"seconds"但实际数据每分钟一条:bucket 内稀疏,device索引的每个键指向大量空时间片,跳转成本高 - 设为
"minutes"且数据均匀:一个 bucket 存约 60 条,device索引键对应紧凑数据块,$match+$sort延迟明显下降 - TTL 清理不能只依赖
ts单字段索引:那样 MongoDB 会遍历全部 bucket。必须建复合 TTL 索引,例如db.collection.createIndex({ device: 1, ts: 1 }, { expireAfterSeconds: 2592000 })
MongoDB 5.0 的硬性限制要避开
这些操作在 5.0 中直接失败或无效:
- 禁止在
metaField上建sparse: true索引(会报错) - 不支持
text、2d、unique索引类型 - 没有
_id默认索引——如果靠_id查询,必须自己建{ _id: 1 } - 无法使用部分索引(
partialFilterExpression):该功能 6.0 才引入,5.0 忽略该选项
最常被忽略的一点:即使你只按 device 查,也别省略 ts 字段在复合索引里的位置——单字段 device 索引在 bucket 粒度下效果有限,而 { device: 1, ts: -1 } 能让 MongoDB 直接定位到目标 bucket 内的倒序头几条,跳过解压和扫描。


















