从节点聚合更慢的主因是索引未预热、readconcern默认保守及wiredtiger缓存分配不合理:索引页常驻内存不足致磁盘扫描增多;从节点未显式设readconcern:"local"引发额外校验开销;缓存未针对索引访问优化导致抖动。

从节点执行聚合比主节点慢的常见原因
不是复制延迟导致的“看起来慢”,而是从节点上聚合操作本身执行更耗时——这通常指向资源隔离、索引状态或读取路径差异。主节点写入压力大但索引热,从节点常被用作只读分析节点,却忽略了它在物理资源和数据加载行为上的天然劣势。
$match 阶段无法命中索引(从节点索引未预热)
从节点启动后或长时间低负载时,索引页可能未载入内存。即使 db.collection.getIndexes() 显示索引存在,explain("executionStats") 会暴露 totalKeysExamined 远高于 nReturned,说明大量索引键扫描发生在磁盘上。
- 主节点因持续写入+查询,B-tree 索引节点常驻内存;从节点若只跑定时报表,索引页很可能已换出
- 验证方式:在从节点执行
db.collection.explain("executionStats").aggregate([{$match: {status: "done"}}]),对比executionTimeMillis和totalKeysExamined - 临时缓解:在业务低峰期对关键索引执行一次全量扫描,如
db.collection.find({status: {$exists: true}}).hint({"status": 1}).itcount(),强制加载索引页
从节点默认禁用 readConcern: "majority" 以外的优化路径
MongoDB 4.2+ 在主节点上,readConcern: "local" 可跳过 oplog 一致性检查,加速聚合;但从节点若配置了 readPreference: "secondary" 且未显式指定 readConcern,驱动可能回退到更保守的读取模式,触发额外的版本校验和快照构建开销。
- 现象:相同聚合命令在从节点加
{readConcern: {level: "local"}}后耗时下降 30%~60% - 风险提示:
readConcern: "local"可能读到尚未同步到多数节点的数据,仅适用于容忍短暂不一致的统计场景 - PyMongo 示例:
collection.aggregate(pipeline, read_preference=ReadPreference.SECONDARY, read_concern=ReadConcern("local"))
从节点的 WiredTiger 缓存未针对聚合优化
WiredTiger 默认将缓存按比例分配给索引(cache for indexes)和数据(cache for data)。聚合密集型负载需要更多索引缓存来加速 $match 和 $sort,但从节点若沿用主节点的通用配置(如 50% 分配给索引),实际聚合中索引访问频次更高,容易引发缓存抖动。
- 查当前分配:
db.serverStatus().wiredTiger.cache["maximum bytes configured"]和相关指标 - 调整建议:在从节点配置文件中增加
wireTiger.engineConfig.configString = "cache_size=8G,eviction_target=80,eviction_trigger=90",并配合应用层预热 - 注意:该参数需重启生效,且不能超过物理内存的 60%,否则引发系统级 swap
真正卡住性能的,往往不是管道写得不够“炫”,而是从节点上那几MB没载入内存的索引页,或者一次没声明 readConcern 的读取请求——这些细节在 explain 输出里藏得不深,但影响极重。











