You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MongoDB时间序列:如何保留每日最后一条记录并清理其余数据?

MongoDB时间序列股票数据留存的优化方案

针对你遇到的问题,这里提供几个比“双集合分开查询”更高效的解决方案:

方案1:定时归档+TTL+聚合合并查询

这是最直接的优化方案,核心思路是自动归档每日最后一条数据到历史集合,同时用TTL清理主集合的过期数据,最后通过聚合操作一次获取全量数据:

  1. 设置主集合TTL:给时间序列集合的时间字段(比如timestamp)配置expireAfterSeconds,值设为2个月对应的秒数(例如5184000,按30天/月计算),确保2个月后自动清理非归档数据。
  2. 定时归档每日最后一条记录:每天执行一次聚合任务,提取前一天的最后一条股票数据,插入到历史集合(比如stock_daily_last)。示例聚合脚本:
    // 提取前一天的最后一条记录并插入历史集合
    const yesterdayEnd = new Date();
    yesterdayEnd.setDate(yesterdayEnd.getDate() - 1);
    yesterdayEnd.setHours(23, 59, 59, 999);
    
    const yesterdayStart = new Date(yesterdayEnd);
    yesterdayStart.setHours(0, 0, 0, 0);
    
    const lastDailyRecord = db.stock_prices.aggregate([
      { $match: { timestamp: { $gte: yesterdayStart, $lte: yesterdayEnd } } },
      { $sort: { timestamp: -1 } },
      { $limit: 1 }
    ]).toArray()[0];
    
    if (lastDailyRecord) {
      db.stock_daily_last.insertOne(lastDailyRecord);
    }
    
    可以用MongoDB Atlas触发器、crontab(结合mongosh脚本)或其他定时任务工具执行这个脚本。
  3. 一次查询获取全量数据:用$unionWith聚合操作符合并主集合(近2个月数据)和历史集合(更早的每日数据),无需两次查询:
    // 查询指定时间范围的所有数据(合并主集合和历史集合)
    const targetStartDate = ISODate("2024-01-01T00:00:00Z");
    
    db.stock_prices.aggregate([
      // 取主集合中符合时间范围的数据
      { $match: { timestamp: { $gte: targetStartDate } } },
      // 合并历史集合中更早的数据
      { $unionWith: {
          coll: "stock_daily_last",
          pipeline: [ { $match: { timestamp: { $lt: targetStartDate } } } ]
        }
      },
      // 按时间排序
      { $sort: { timestamp: 1 } }
    ])
    

方案2:实时归档(基于Change Streams)

如果需要实时维护每日最后一条记录的准确性(避免定时任务的延迟),可以用MongoDB的Change Streams监听主集合的插入事件,实时更新历史集合的当日记录:

// 监听主集合的插入操作
const changeStream = db.stock_prices.watch([
  { $match: { operationType: "insert" } }
]);

changeStream.on("change", (change) => {
  const newRecord = change.fullDocument;
  // 提取日期键(如"2024-05-20")
  const dateKey = newRecord.timestamp.toISOString().split("T")[0];
  
  // 更新历史集合:如果当日已有记录则替换为最新的,否则插入
  db.stock_daily_last.updateOne(
    { date: dateKey },
    { $set: {
        timestamp: newRecord.timestamp,
        price: newRecord.price,
        symbol: newRecord.symbol
        // 其他需要保留的字段
      }
    },
    { upsert: true }
  );
});

这种方式下,历史集合始终保存当日最新的最后一条记录,主集合的TTL策略保持不变,查询时同样用$unionWith合并数据。

方案3:桶级保留(进阶)

MongoDB时间序列集合以桶为单位存储数据,每个桶对应一个时间范围。对于超过2个月的桶,我们可以:

  1. 用聚合提取桶内的最后一条记录
  2. 将这条记录插入历史集合
  3. 删除整个过期桶
    这种方式避免了主集合中留存大量过期的细粒度数据,但需要额外处理桶的识别和删除逻辑,适合对存储占用要求极高的场景。

内容的提问来源于stack exchange,提问作者Mehdi Eskandari

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 12:12:46