MongoDB 从 5.0 版本开始提供原生时间序列集合,适合存储监控指标、物联网传感器数据、行情数据等按照时间先后不断追加的记录。创建一个时间序列集合时,除了集合名称,还必须声明 timeseries 选项,其中 timeField 指定时间字段,metaField 指定用于分组的元数据字段,granularity 控制桶的粒度。这些信息不会写入每个文档,而是作为集合级别的元数据保存在 MongoDB 的 catalog 中。当这份元数据缺失或无法读取时,数据库就会抛出错误码 2060,错误信息通常为:TimeSeriesMetadataMissing。出现这个问题的场景,大多和备份恢复、数据迁移、集群降级或使用了不兼容的工具链有关。

时间序列元数据与 2060 错误的关系
MongoDB 的时间序列集合在物理上并不是一个普通集合,而是会在 system.buckets 命名空间中创建对应的内部桶集合。你在业务代码中看到的 sensor_data 集合,读取时是由 MongoDB 根据 timeseries 元数据自动将桶集合展开,然后再按时间字段返回文档。创建集合时写入的 timeField、metaField、granularity 等信息就存放在 catalog 的 collection options 中。如果 options 里没有 timeseries 这一项,服务器就无法建立从逻辑集合到物理桶集合的映射,于是会以错误码 2060 表示元数据缺失。
这个错误并不是说磁盘上的时间序列数据一定已经删除。事实上,很多案例中 system.buckets.<集合名> 内部仍然保留着分桶后的数据,甚至用 find 还能看到 control、data、meta 等字段。但由于集合定义已经退化成普通集合,服务器在初始化访问路径时拿不到 timeField 和 metaField,就会拒绝继续执行。我们可以用 db.getCollectionInfos({ name: "sensor_data" }) 查看集合的 options,正常情况下能看到类似下面的结构:
db.getCollectionInfos({ name: "sensor_data" }).forEach(function(info) {
print(JSON.stringify(info.options, null, 2));
});
// 正常时间序列集合的 options 中应包含:
// {
// "timeseries": {
// "timeField": "ts",
// "metaField": "device_id",
// "granularity": "seconds"
// }
// }
如果返回结果只有空对象或者普通索引选项,而该集合本应是一个时间序列集合,那么 2060 错误的原因就可以确认。通常这意味着在恢复或迁移过程中只恢复了文档数据,没有恢复集合元数据。MongoDB 不允许直接给一个已经存在的普通集合添加 timeseries 选项,因此这类问题不能通过 collMod 直接修正,必须走重建流程。
触发 2060 的常见路径
最典型的情况是使用 mongorestore 时带了 --noOptionsRestore 参数。这个参数会告诉工具只恢复数据和索引,不恢复集合选项。对于普通集合影响不大,但对于时间序列集合却会直接导致 timeseries 元数据丢失。恢复完成后,集合变成普通集合,应用一旦尝试写入或执行时间序列相关查询,就会收到 2060。另一个常见触发点是跨版本迁移:用 MongoDB 4.4 时代的 mongodump 备份 5.0 及以上版本的时间序列集合,旧工具不识别新的集合选项,恢复时自然也不会包含 timeseries 定义。
手动复制数据文件也可能造成同样的不一致。有人为了快速扩容或迁移,会直接把 WiredTiger 目录下的 collection-*.wt 和 index-*.wt 文件拷贝到新节点。时间序列集合还需要 catalog 中的元数据,如果只拷贝文件而不包括 catalog 信息,新节点启动后可能无法识别完整定义。集群降级也需要注意:时间序列集合只能在 MongoDB 5.0 及以上版本使用,如果把包含时间序列集合的库降级到 4.4,也会出现元数据无法解释的问题。
--noOptionsRestore恢复后集合退化为普通集合- 使用旧版 mongodump 或第三方 GUI 工具只导出数据不导出选项
- 手工迁移数据文件,catalog 信息没有同步
- 将时间序列集合所在集群降级到 4.4 以下版本
- 开发或测试环境误删了 catalog 中的元数据
如何定位并修复 2060 错误
定位问题的第一步是确认集合定义是否完整。执行 db.getCollectionInfos({ name: "sensor_data" }),检查 options.timeseries 是否存在。如果不存在,再看底层是否存在 system.buckets.sensor_data 集合。可以使用 db.getSiblingDB("app").getCollection("system.buckets.sensor_data").find().limit(1) 确认数据还在不在。只要桶数据还在,恢复思路就比完全丢失要清晰得多,但直接操作 system.buckets 并不是官方推荐的做法,而且桶的内部结构在不同版本中可能有差异,所以优先使用完整备份恢复。
如果有完整的 mongodump 备份,恢复时一定不要使用 --noOptionsRestore。正确命令如下:
# 备份数据库和集合选项 mongodump --db app --collection sensor_data --out /data/backup # 恢复时使用默认选项,保留 timeseries 元数据 mongorestore --db app --collection sensor_data /data/backup/app/sensor_data.bson
如果备份本身已经缺失元数据,或者原始数据还可以从业务上游重新采集,那么重建集合是更可靠的方式。先确认业务可以短暂接受写入中断,然后把现有数据导出,删除问题集合,按照原来的时间字段和元数据字段重新创建时间序列集合,最后回填数据。重建命令如下:
// 导出当前集合中的数据
mongodump --db app --collection sensor_data --out /tmp/rebuild
// 删除问题集合
db.sensor_data.drop();
// 重新创建时间序列集合
db.createCollection("sensor_data", {
timeseries: {
timeField: "ts",
metaField: "device_id",
granularity: "seconds"
}
});
// 如果数据格式匹配,使用 mongorestore 导入
mongorestore --db app --collection sensor_data /tmp/rebuild/app/sensor_data.bson
重建完成后不要只检查是否有数据,还要验证元数据是否真正恢复。执行 db.getCollectionInfos({ name: "sensor_data" }) 确认 options.timeseries.timeField 的值符合预期,再插入一条测试文档,执行一次按时间范围的查询,确认不再出现 2060。也可以运行 db.runCommand({ collStats: "sensor_data" }),返回结果中如果包含 timeseries 统计信息,就说明集合已经正确识别。
运维层面的预防措施
时间序列集合对备份工具版本比较敏感。无论使用 mongodump、mongorestore、Ops Manager 还是云厂商的备份服务,都要确认工具链版本与服务器版本匹配。尤其是升级到 MongoDB 5.0 或更高版本后,不要继续使用 4.4 时代的备份脚本。安全一点的做法是,在恢复或迁移完成后增加一个自动校验步骤,用 db.getCollectionInfos 检查所有时间序列集合是否仍然保留 timeseries 选项,避免带病上线。
不要直接操作系统内部的 system.buckets 集合。虽然排查时可以少量读取,但直接对其进行写入、删除或建索引都有可能导致桶内数据与元数据不一致,进而触发更复杂的问题。时间序列集合的写入、更新、删除和索引创建都应该通过业务集合本身的接口完成。MongoDB 还支持 expireAfterSeconds 选项用于自动过期,也应提前设计,避免后续频繁使用 collMod 修改。
此外,如果规划过 Windows 环境下的自动化脚本,路径要写成 C:\data\backup 这种带反斜杠的形式,并在 PowerShell 或批处理中正确引用,避免路径解析失败导致备份没有真正执行。日常巡检中建议记录每种时间序列集合的创建选项,一旦出现 2060,重建时可以快速恢复定义,不必猜测 timeField 或 metaField 名称。
MongoDB错误码2060时间序列元数据集合重建修改时间:2026-09-30 15:03:14