时间序列集合必须在创建后、插入数据前用sh.shardcollection()显式分片并指定timeseries配置,分片键仅限metafield内字段,timefield不可用;6.0不支持重新分片,distinct失效需用聚合替代。

时间序列集合必须用 sh.shardCollection() 分片,不能用 sh.enableSharding() 单独启用
直接对数据库调用 sh.enableSharding("db") 只是让数据库支持分片,但时间序列集合不会自动分片——它仍会写入主分片(primary shard),且后续 sh.shardCollection() 会失败,报错 "cannot shard collection with timeseries options on an already sharded collection"。必须在集合创建后、插入任何数据前完成分片操作。
常见错误现象:先插入数据再分片,或误用 sh.shardCollection() 时漏传 timeseries 选项,导致命令静默成功但实际未生效(集合仍是未分片状态)。
- 确认数据库已启用分片:
sh.status()中能看到该数据库的"enabled": true - 分片命令必须显式包含
timeseries配置对象,哪怕只是空对象(如{ timeseries: {} }),否则 MongoDB 不识别为时间序列分片请求 - 分片键字段(如
"metadata.sensorId")必须已在timeseries配置中通过metaField声明,且该字段值需在插入文档时存在
分片键只能是 metaField 路径下的字段,不能是 timeField
时间序列集合的分片键受限严格:只允许使用 metaField 所指定对象内的字段路径(例如 metaField: "metadata" → 可用 "metadata.site" 或 "metadata.sensorId"),timeField(如 "timestamp")及其子字段**完全不可用**作分片键。尝试使用会报错 "timeField cannot be part of the shard key"。
原因在于时间序列的物理存储模型——数据按时间连续写入桶(bucket),timeField 是桶内排序依据,不是跨桶路由依据。强行用时间字段分片会导致桶被强制拆散,破坏压缩与查询效率。
- 若业务需要按时间范围路由(如“2024年数据去分片A”),应改用
zones+ 哈希分片键(如{"sensorId": "hashed"}),再为不同哈希段绑定 zone - 复合分片键如
{"metadata.site": 1, "metadata.sensorId": 1}是允许的,但所有字段都必须落在metaField对象内 - 分片键字段值不能为
null或缺失,否则文档会被拒绝写入
MongoDB 6.0 不支持对已有时间序列集合重新分片
虽然 MongoDB 8.0+ 支持 reshardCollection 时间序列集合,但 6.0 完全不提供该能力。一旦用 sh.shardCollection() 指定了分片键,就无法更改——试图再次调用会报错 "collection already sharded"。
这意味着分片键选型错误(如用了低基数 metadata.region 导致数据倾斜)时,唯一可行方案是:导出数据 → 删除集合 → 重建带新分片键的时间序列集合 → 重新导入。整个过程需停写,且 $out 聚合不能直接写入时间序列集合(仅支持 $merge 输出到普通集合)。
- 导出可用
mongodump --uri="..." --collection=weather - 重建命令示例:
sh.shardCollection("test.weather", {"metadata.sensorId": 1}, {timeseries: {timeField: "timestamp", metaField: "metadata", granularity: "hours"}}) - 导入前确保目标集合为空,否则
mongorestore会跳过已存在集合
distinct 在时间序列集合上失效,得用 $group 替代
即使分片成功,db.weather.distinct("metadata.site") 也会返回空数组或超时——因为时间序列集合底层不维护 _id 索引,且 distinct 命令无法适配其桶式存储结构。这不是分片问题,而是类型限制。
正确做法是走聚合管道:先用 $match 过滤必要范围(减少跨分片扫描),再用 $group 提取唯一值。注意必须在 metaField 相关字段上建好索引,否则聚合会在所有分片上全表扫描。
- 建索引:
db.weather.createIndex({"metadata.site": 1}) - 聚合替代:
db.weather.aggregate([{$match: {"metadata.timestamp": {$gte: ISODate("2024-01-01")}}}, {$group: {_id: "$metadata.site"}}]) - 若需统计数量,
$group内加count: {$sum: 1};避免用$addToSet,它在分片环境下不保证最终一致性
distinct 类操作的失效不是 bug,是时间序列存储模型的必然代价——用空间换时间,就得接受某些便利功能的缺席。











