
时序数据存储概述时序数据是按时间顺序排列的数据点集合广泛应用于物联网、监控系统、金融分析等领域。与传统数据不同时序数据具有高写入频率、高数据量、按时间范围查询等特性。MongoDB 作为文档型 NoSQL 数据库凭借其灵活的模式和良好的扩展性成为处理时序数据的常用选择。然而随着数据量的持续增长直接存储原始时序数据会导致存储空间迅速膨胀、查询性能下降等问题。因此需要采用适合时序数据特点的存储策略。MongoDB 时序数据存储方案对比2.1 Bucketing 分桶存储Bucketing 是一种将时间相近的数据点聚合存储在单个文档中的技术。通过按时间窗口如每小时、每天将数据分桶可以有效减少文档数量提高存储效率和查询性能。// 创建时序集合的示例 db.createCollection(sensor_readings, { timeseries: { timeField: timestamp, metaField: metadata, granularity: seconds // 可选: minutes, hours } }); // 插入时序数据示例 db.sensor_readings.insertOne({ timestamp: new Date(), metadata: { device_id: sensor1, location: room1 }, temperature: 25.3, humidity: 60.2 });Bucketing 方案的优势在于减少文档数量降低索引开销提高范围查询性能特别是按时间窗口查询适合高频写入、长期存储的场景局限性包括需要合理选择时间窗口大小窗口过大会丢失精度窗口过小则效果不明显跨窗口查询可能需要处理多个文档2.2 TTL 索引自动过期TTL (Time To Live) 索引允许为文档设置自动过期时间非常适合需要定期清理旧数据的时序场景。// 创建 TTL 索引 db.sensor_readings.createIndex({ timestamp: 1 }, { expireAfterSeconds: 2592000 // 30天后自动删除 }); // 或者更新集合设置 TTL db.runCommand({ collMod: sensor_readings, index: { keyPattern: { timestamp: 1 }, expireAfterSeconds: 2592000 } });TTL 索引的优势自动清理过期数据无需手动维护实现简单只需在时间字段上创建索引适合需要定期清理旧数据的场景局限性仅基于时间字段无法基于业务逻辑条件删除删除操作不是实时的有延迟无法保留长期历史数据分析能力2.3 降采样技术降采样是通过聚合算法减少数据点数量同时保留数据趋势和特征的技术。在 MongoDB 中可以通过聚合管道实现降采样。// 按小时降采样示例 db.sensor_readings.aggregate([ { $group: { _id: { year: { $year: $timestamp }, month: { $month: $timestamp }, day: { $dayOfMonth: $timestamp }, hour: { $hour: $timestamp } }, avgTemp: { $avg: $temperature }, minTemp: { $min: $temperature }, maxTemp: { $max: $temperature }, count: { $sum: 1 } } }, { $sort: { _id.year: 1, _id.month: 1, _id.day: 1, _id.hour: 1 } } ]);降采样的优势显著减少数据存储空间保留长期数据趋势支持历史分析可根据不同业务需求选择不同粒度的聚合方式局限性丢失原始数据的细节信息需要额外存储降采样后的数据可能增加数据处理的复杂度2.4 三种方案对比特性Bucketing 分桶存储TTL 索引自动过期降采样技术存储效率高减少文档数量中仅删除过期数据极高减少数据点查询性能高按时间窗口查询快中需过滤过期数据中聚合后查询数据精度中窗口大小决定精度低自动删除历史数据低聚合丢失细节实现复杂度中需配置时间窗口低简单索引高需编写聚合逻辑适用场景高频写入、中短期查询需要定期清理的数据长期趋势分析、历史数据数据保留可配置保留期限自动按时间删除可配置不同粒度保留方案选择与最佳实践根据业务需求和数据特点可以选择单一方案或组合使用数据量小、查询频率低使用 TTL 索引自动清理过期数据数据量大、高频写入、中短期查询采用 Bucketing 分桶存储需要长期保留数据并进行趋势分析使用降采样技术复杂场景组合使用三种方案如近期数据使用 Bucketing历史数据使用降采样配合 TTL 索引管理数据生命周期分析数据特点数据量大小查询频率数据保留需求小数据量大数据量低频查询高频写入查询短期查询长期趋势分析TTL 索引方案Bucketing 方案降采样技术组合方案实施创建TTL索引实施配置时间窗口分桶实施设计聚合管道实施混合策略配置最小示例与注意事项以下是一个结合三种方案的完整示例// 1. 创建时序集合 db.createCollection(iot_data, { timeseries: { timeField: timestamp, metaField: device_info, granularity: minutes } }); // 2. 创建 TTL 索引保留最近90天数据 db.iot_data.createIndex({ timestamp: 1 }, { expireAfterSeconds: 7776000 // 90天 }); // 3. 插入传感器数据 for (let i 0; i 1000; i) { db.iot_data.insertOne({ timestamp: new Date(Date.now() - i * 60000), // 每分钟一条数据 device_info: { device_id: sensor${i % 10}, location: room${Math.floor(i / 10)} }, value: Math.random() * 100, status: active }); } // 4. 按天降采样查询最近7天数据 db.iot_data.aggregate([ { $match: { timestamp: { $gte: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000) } } }, { $group: { _id: { day: { $dateTrunc: { date: $timestamp, unit: day } }, device_id: $device_info.device_id }, avgValue: { $avg: $value }, minValue: { $min: $value }, maxValue: { $max: $value }, count: { $sum: 1 } } }, { $sort: { _id.day: 1, _id.device_id: 1 } } ]); // 5. 按小时查询特定设备数据 db.iot_data.find({ device_info.device_id: sensor1, timestamp: { $gte: new Date(Date.now() - 24 * 60 * 60 * 1000), $lt: new Date() } }).sort({ timestamp: 1 });注意事项Bucketing 时间窗口选择应根据数据写入频率和查询需求平衡通常建议为查询时间单位的2-5倍TTL 索引删除操作不是实时的会有延迟不适合需要精确数据保留期的场景降采样应保留必要的统计信息如平均值、最大值、最小值等以便后续分析对于关键业务数据建议结合使用多种策略如近期数据使用 Bucketing 保留原始数据历史数据使用降采样定期监控集合大小和索引效率根据实际使用情况调整存储策略