$lookup是mongodb聚合管道中实现跨集合左外连接的核心阶段,它在每篇文档中新增字段嵌入匹配的关联集合文档,不改变原文档结构。

大聚合压垮从节点:为什么 secondary 会跟不上
直接原因很朴素:从节点不是“只读”,它得一边回放主节点的 oplog,一边响应客户端查询。当主节点上跑一个耗时长、内存占用高的 $group 或 $lookup 聚合时,oplog 积压会立刻显现——从节点同步线程被查询线程抢占,复制延迟(optimeDate 差值)飙升到秒级甚至分钟级。
这不是配置错,而是资源争抢的必然结果。尤其在以下场景更明显:
- 聚合未加
allowDiskUse: true,导致内存爆满后整个 mongod 进程卡顿 - 从节点硬件弱于主节点(比如 CPU 核数少、磁盘是 HDD)
- 聚合输出大量文档,触发大量写入 oplog 的反向传播(如带
$out写回同库)
禁止在从节点执行高开销聚合
最简单有效的解法,是让聚合只在主节点发生。别依赖 readPreference=secondaryPreferred 自动分流——它不区分查询类型,会把重聚合也扔给从节点。
实操建议:
- 业务代码中对明确知道是“报表类”“导出类”“后台统计类”的聚合操作,显式指定
readPreference=primary - 在 Mongos 层(如果是分片集群)或应用连接池里,为这类操作单独建一个只连 primary 的 client 实例
- 用
db.setProfilingLevel(1, {slowms: 500})捕获慢聚合,再结合db.currentOp({secs_running: {$gt: 2}})定期巡检,确认是否真有聚合在从节点上跑
优化聚合本身比调副本集参数更治本
很多团队花时间调 writeConcern 或 readConcern,却忽略聚合语句本身是否可收敛。延迟根源常在聚合设计里。
关键检查点:
- 是否在
$match阶段就加了能走索引的条件?没索引的$match等价于全表扫,从节点同步压力翻倍 - 是否用了
$unwind膨胀大量文档?考虑前置聚合或拆成两阶段,避免中间数据爆炸 - 是否用
$lookup关联大集合?确认被关联集合有对应字段的索引,否则从节点要反复扫描 - 是否遗漏
collation导致排序无法利用索引?特别是多语言环境下的字符串比较
临时救急:降权 + 隐藏 + 独立重建索引
如果聚合已引发持续延迟,且无法立刻改代码,可用滚动隔离法保主从同步链路:
- 先在主节点运行
rs.conf()查出目标从节点索引(如cfg.members[2]) - 执行
cfg.members[2].priority = 0; cfg.members[2].hidden = true; rs.reconfig(cfg, {force: true})将其踢出选举和读路由 - 登录该节点服务器,停掉服务,用
mongod --port 27018 --dbpath /data/db --bind_ip localhost启为独立实例 - 在独立实例上跑聚合,或重建缺失索引(务必加
background: true) - 完成后停独立实例,恢复原配置并
rs.reconfig()重新加入副本集
这个方法绕开了复制流干扰,但要注意:独立实例期间该节点不参与任何数据同步,恢复后需等待完整追赶(rs.printSlaveReplicationInfo() 观察 syncedTo)——别在大流量时段操作。











