MongoDB 6.0 默认启用 allowDiskUse 并支持跨分片 $lookup,但需注意磁盘空间监控、分片键对齐、$match 必须置首、$$SEARCH_META 仅在 $search 后立即使用才有效。

allowDiskUse 默认启用但得盯住磁盘空间
MongoDB 6.0 起,allowDiskUse 默认开启,聚合阶段(如 $group、$sort)内存超 100MB 时自动写临时文件到磁盘,不再报错。这省了手动加 { allowDiskUse: true } 的麻烦,但容易忽略副作用:
- 临时文件默认写入
/tmp或dbPath下的journal子目录,磁盘满会导致聚合失败甚至影响其他操作 - 分片集群中,各分片独立写本地磁盘,监控必须覆盖所有节点
- 若明确不允许落盘(比如容器环境磁盘极小),需显式传
{ allowDiskUse: false },否则静默写盘后才发现空间告警
$lookup 在分片集群里能用,但关联集合必须“对齐”
$lookup 在 6.0 才真正支持跨分片执行,但有个硬约束:被关联的 from 集合要么是**非分片集合**,要么和主集合**共享完全相同的分片键**。否则会退化为广播查询——每个分片都拉全量数据再本地匹配,性能断崖下跌。
- 错误示例:
{ $lookup: { from: "users", localField: "user_id", foreignField: "_id" } },而users按region分片,主集合按user_id分片 → 触发全分片扫描 - 正确做法:提前在
users上建{ user_id: 1 }索引,并确保它和主集合分片键一致;或把users改成非分片集合 - 别在
$lookup后立刻$unwind大数组——返回的关联结果可能膨胀数倍,极易触发内存超限,先$limit或$sample控制数量
$match 必须放管道最前,索引才生效
$match 阶段只有出现在聚合管道**第一个位置**,才能利用集合上的索引快速过滤。一旦前面有 $project、$addFields 或 $unwind,索引就失效——因为文档结构已变,数据库无法映射回原始索引字段。
- 高效写法:
[ { $match: { status: "active", created_at: { $gte: ... } } }, { $group: ... } ]→ 可走{ status: 1, created_at: 1 }复合索引 - 低效写法:
[ { $project: { status: 1, created_at: 1 } }, { $match: { status: "active" } } ]→ 全表扫描,$project白做 - 优化器虽会尝试把后续
$match拆解并前移,但仅限于不依赖投影计算的字段(比如name: "Joe"可前移,avgTime: { $gt: 7 }就不行)
$$SEARCH_META 只在 $search 之后、$lookup 之前有效
$$SEARCH_META 是 Atlas Search 提供的元信息变量,但在 6.0 中生命周期极短:仅在 $search 阶段输出后、且**尚未进入 $lookup 或 $unionWith 阶段前**可用。顺序错一点,变量就变成 undefined,且不报错,只返回空值。
- 错误顺序:
{ $search: ... }, { $lookup: ... }, { $addFields: { score: "$$SEARCH_META.score" } }→score字段为空 - 正确顺序:
{ $search: ... }, { $addFields: { searchScore: "$$SEARCH_META.score" } }, { $lookup: ... }→ 提前固化元信息 - 如果关联后还需用搜索得分,只能靠
$addFields提前存成普通字段,不能指望后期再取$$SEARCH_META
allowDiskUse 的静默落盘和 $$SEARCH_META 的阶段时效性——一个占满磁盘,一个返回空值,都不报错,查日志也看不出问题,得靠阶段顺序和部署环境双重校验。


















