如何优化Mongo聚合管道,避免OOM并获取各事件类型最近5条文本?
聚合管道OOM问题优化方案:获取各event_type最近5条text样本
原管道在处理百万级文档时,$group阶段会将每个event_type下的所有text存入数组,导致内存占用过高引发OOM。以下是针对性优化方案:
方案1:先排序再分组,仅保留最新5条text
先按event_type分组前,将同类型文档按时间倒序排序,分组时只保留最新的5条text,避免积累大量无效数据:
Events.aggregate([ { $match: { event_type: { $in: [1,5,10,12] } } }, // 同event_type内按time倒序排列,确保最新文档在前 { $sort: { event_type: 1, time: -1 } }, { $group: { _id: "$event_type", avgTime: { $avg: "$time" }, avgCost: { $avg: "$cost" }, maxCost: { $max: "$cost" }, maxTime: { $max: "$time" }, // 每次push后立即截取前5条,避免数组无限膨胀 texts: { $slice: [{ $push: "$text" }, 5] } } } ])
方案2:创建复合索引加速查询与排序
针对$match和$sort阶段创建复合索引,减少磁盘IO与内存排序开销:
// 创建复合索引 Events.createIndex({ event_type: 1, time: -1 })
该索引可直接用于$match过滤指定event_type,同时让$sort阶段无需在内存中排序,大幅降低内存占用。
方案3:拆分任务,单event_type单独处理
如果聚合统计与样本获取可以拆分,可对每个event_type单独执行查询,避免一次性处理所有数据:
// 遍历每个event_type,分别查询统计与最新text const eventTypes = [1,5,10,12]; const results = await Promise.all(eventTypes.map(async (type) => { // 获取最新5条text const latestTexts = await Events.find({ event_type: type }) .sort({ time: -1 }) .limit(5) .map(doc => doc.text) .toArray(); // 计算聚合统计 const stats = await Events.aggregate([ { $match: { event_type: type } }, { $group: { _id: null, avgTime: { $avg: "$time" }, avgCost: { $avg: "$cost" }, maxCost: { $max: "$cost" }, maxTime: { $max: "$time" } } } ]).next(); return { event_type: type, avgTime: stats.avgTime, avgCost: stats.avgCost, maxCost: stats.maxCost, maxTime: stats.maxTime, texts: latestTexts }; }));
方案4:使用窗口函数(MongoDB 5.0+)
利用$setWindowFields窗口函数,在计算聚合统计的同时高效获取最新N条数据,避免$group阶段的大数组:
Events.aggregate([ { $match: { event_type: { $in: [1,5,10,12] } } }, { $sort: { event_type: 1, time: -1 } }, { $setWindowFields: { partitionBy: "$event_type", sortBy: { time: -1 }, output: { avgTime: { $avg: "$time", window: { documents: ["unbounded", "current"] } }, avgCost: { $avg: "$cost", window: { documents: ["unbounded", "current"] } }, maxCost: { $max: "$cost", window: { documents: ["unbounded", "current"] } }, maxTime: { $max: "$time", window: { documents: ["unbounded", "current"] } }, // 标记同组内的文档排名 rank: { $rank: {} } } } }, // 仅保留每个event_type的前5条文档 { $match: { rank: { $lte: 5 } } }, { $group: { _id: "$event_type", avgTime: { $first: "$avgTime" }, avgCost: { $first: "$avgCost" }, maxCost: { $first: "$maxCost" }, maxTime: { $first: "$maxTime" }, texts: { $push: "$text" } } } ])
内容的提问来源于stack exchange,提问作者Erik
相关产品推荐
相关产品推荐

