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

MongoDB执行aggregate与$group查询时内存溢出问题求优化方案

MongoDB亿级allvoice集合聚合报错优化方案

报错核心原因

  • MongoDB存在16MB单个BSON文档硬限制,你当前的聚合逻辑是将单日所有通话记录通过$push存入单个文档的data数组内,当日通话量超过约8万条(按单条记录200字节估算)就会超出该限制,这是报错的根本原因,和服务器配置无关,即使开启allowDiskUse也无法突破该限制。
  • 你后续优化的按月查询逻辑没有解决单文档大小超限的核心问题,当月单日数据量过大时仍会触发报错。

优化方案

方案1:拆分存储结构(最推荐,无大小限制性能最优)

放弃将单日所有记录塞入单个数组的设计,拆分为两个集合分别存储统计指标和明细数据:

  1. dailyvoice_stats:按天存储统计指标,单天仅1条记录,无大小超限风险
  2. dailyvoice_details:存储单条通话明细,每条新增date(格式为YYYY-MM-DD)字段,对date建立索引后按天查询性能和原方案一致,且无大小限制

优化后聚合语句

首先跑明细数据(按月分批执行,避免全量压力):

// 单月明细数据聚合
db.getCollection('allvoice').aggregate([
    // 按月过滤,提前给month字段建索引可大幅提升过滤速度
    { $match: { month: "202001" } },
    {
        $addFields: {
            // 直接从originaltimestamp字符串截取拼接日期,比两次类型转换效率高30%以上
            "date": {
                $concat: [
                    { $substr: ["$originaltimestamp", 0, 4] },
                    "-",
                    { $substr: ["$originaltimestamp", 4, 2] },
                    "-",
                    { $substr: ["$originaltimestamp", 6, 2] }
                ]
            }
        }
    },
    // 可按需保留需要的字段,减少写入数据量
    {
        $project: {
            subscriber_id: 1,
            callednumber: 1,
            originaltimestamp: 1,
            duration: 1,
            maincost: 1,
            type: 1,
            type_network: 1,
            month: 1,
            date: 1
        }
    },
    { $out: "dailyvoice_details_202001" }
], { allowDiskUse: true })

再跑统计数据:

// 单月统计数据聚合
db.getCollection('allvoice').aggregate([
    { $match: { month: "202001" } },
    {
        $group: {
            _id: {
                $concat: [
                    { $substr: ["$originaltimestamp", 0, 4] },
                    "-",
                    { $substr: ["$originaltimestamp", 4, 2] },
                    "-",
                    { $substr: ["$originaltimestamp", 6, 2] }
                ]
            },
            count: { $sum: 1 },
            total_duration: { $sum: { $toDouble: "$duration" } },
            total_cost: { $sum: { $toDouble: "$maincost" } }
            // 可按需新增其他统计维度
        }
    },
    { $out: "dailyvoice_stats_202001" }
], { allowDiskUse: true })

使用示例

按天查询明细:

// 给date建索引后查询性能极高
db.dailyvoice_details_202001.find({date: "2020-01-13"})

按天查询统计值:

db.dailyvoice_stats_202001.find({_id: "2020-01-13"})

方案2:保留原结构的折中方案

如果必须保留单日一条带数组的结构,可将单日数据按小时分桶拆分,降低单文档大小:

db.getCollection('allvoice').aggregate([
    { $match: { month: "202001" } },
    {
        $group: {
            _id: {
                date: {
                    $concat: [
                        { $substr: ["$originaltimestamp", 0, 4] },
                        "-",
                        { $substr: ["$originaltimestamp", 4, 2] },
                        "-",
                        { $substr: ["$originaltimestamp", 6, 2] }
                    ]
                },
                // 按小时分桶,单文档大小降低为原来的1/24
                hour: { $substr: ["$originaltimestamp", 8, 2] }
            },
            data: {
                $push: {
                    subscriber_id: "$subscriber_id",
                    type: "$type",
                    date: { "$toDate": "$originaltimestamp" },
                    month: "$month"
                }
            },
            count: { "$sum": 1 }
        }
    },
    { $out: 'dailyvoice_202001' }
], { allowDiskUse: true })

额外优化建议

  • 给allvoice集合的month字段建立索引,可大幅提升按月过滤的效率
  • 全量数据同步时按月分批执行,不要一次性跑全量数据,降低数据库压力,出错也只需重跑单月任务
  • 若后续需要跨月查询,可将多个月份的明细/统计集合合并为全局集合,给date、month建联合索引即可

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 03:48:02