“exceeded memory limit for $group”错误主因是聚合管道书写不当——$match未前置、$project未精简字段、$group/$sort过早触发致中间结果膨胀;allowdiskuse:true需显式传参且在分片集群中不自动透传至各shard。

聚合报错“Exceeded memory limit for $group”不是内存不够
这个错误几乎从不因为服务器物理内存不足,而是管道阶段顺序和字段冗余导致中间结果撑爆默认的 100MB 内存上限。MongoDB 不会因为你加了 allowDiskUse: true 就自动绕过所有限制——它只在特定条件下生效,且在分片集群中需显式透传。
$match 必须放在 $group 和 $sort 前面
如果 $match 写在 $group 后面,它只是对分组完的结果再过滤,此时内存早已被全量文档占满。MongoDB 只有第一个 $match 阶段能走索引,后续的 $match 是纯内存扫描。
- 高选择性条件优先:比如
{status: "completed", created_at: {$gte: ISODate("2025-01-01")}},比只写{type: "order"}有效得多 - 复合索引字段顺序必须匹配
$match中键的顺序,例如{shop_id: 1, created_at: 1}才能支撑{$match: {shop_id: ..., created_at: {...}}} -
$lookup或$unwind之前的$match不会被优化器前移,必须手动写到它们前面
$project 是控制内存最直接的开关
$project 不是锦上添花,而是防止内存爆炸的关键动作。原始文档带 description、images、logs 等大字段时,不做投影,一个 20KB 文档 × 5000 条就超限。
- 只保留
$group或$sort实际用到的字段,例如{category: 1, amount: 1, _id: 0} - 显式写
_id: 0,否则默认携带 ObjectId(百万条就是 12MB) - 避免在
$project里计算新字段再用于后续$match,如{avgScore: {$avg: "$scores"}}+{$match: {avgScore: {$gt: 80}}},这会让前置过滤失效
allowDiskUse: true 的真实作用和限制
allowDiskUse: true 是兜底手段,不是性能优化。它允许 MongoDB 把临时排序/分组数据写入磁盘,但前提是调用时必须作为 options 参数显式传入,且在分片集群中不会自动下发到各 shard。
- 正确写法:
db.collection.aggregate([...], {allowDiskUse: true}) - 在事务中不可用,事务内聚合必须靠前置
$limit或精简字段来控量 - 4.2 及以下版本不支持
$push内嵌$sort,升级到 4.4+ 才能用{$push: {$each: [...], $sort: {...}}} - 即便开了
allowDiskUse,如果$group前没$match和$project,还是可能因单个 shard 本地聚合超限而失败
allowDiskUse 不自动穿透到 shard —— 应用端必须显式传参,而不是以为 mongos 会代劳。











