根本原因是读写请求集中打到少数分片或Secondary节点同时承受业务读与oplog复制双重I/O压力,典型表现为await>80ms、%util≈100%而CPU不高;常见诱因包括无片键查询、readPreference=secondaryPreferred下的聚合压测、TTL集中删除及WiredTiger缓存不足。

为什么分片集群磁盘IO突然飙升?
根本原因不是“数据量大”,而是读写请求集中打到少数分片,或 Secondary 同时扛业务读 + oplog 复制双重压力。典型现象是 iostat -x 1 显示 await > 80ms、%util 接近 100%,而 top 看 CPU 并不高——说明瓶颈在磁盘队列,不是计算能力。
常见诱因包括:
- 不带片键的查询(
mongos广播到所有分片,每个分片都触发全表扫描) -
readPreference=secondaryPreferred下大量聚合或getMore操作压垮从节点 I/O - 过期文档自动删除(
TTL index)在高峰期集中触发,产生大量随机 delete + journal 写入 - WiredTiger 缓存不足,
pages read into cache持续上涨,频繁从磁盘加载热数据
如何快速定位高IO分片和表?
别直接看整体 mongostat,先登录每个 mongos 和各分片的 Primary,用以下命令逐层下钻:
- 查当前高耗时操作:
db.currentOp({secs_running: {$gt: 5}}),重点关注secs_running高且secs_running和microsecs_running差值大的操作(说明卡在 I/O 等待) - 查热点集合:
/path/to/mongotop --host shard01:27017 -u user -p pwd --authenticationDatabase=admin 5(每5秒刷新),观察哪张表total时间占比长期超 30% - 查缓存压力:
db.serverStatus().wiredTiger.cache,若bytes currently in the cache长期 > 95%maximum bytes configured,且pages read into cache每秒增长 > 100,就是缓存击穿
关键配置项必须调:cacheSizeGB 和 maxConcurrentOps
storage.wiredTiger.engineConfig.cacheSizeGB 不是可选项,是硬门槛。默认值(系统内存 × 50% − 1GB)在分片集群中往往偏低,尤其当单分片承载多库多集合时。
- 确认当前值:
db.hostInfo().system.memLimitMB和db.serverStatus().wiredTiger.cache["maximum bytes configured"] - 生产建议:分配系统内存的 40%~55%,但上限不超过 32GB(避免 OS 内存回收抖动);例如 64GB 机器设为
24 - Secondary 节点必须压低并发读:
db.runCommand({ setParameter: 1, maxConcurrentOps: 128 })(默认 512 太激进,留足资源给 oplog 应用线程) - 验证是否生效:
db.runCommand({ getCmdLineOpts: 1 })查parsed.setParameter字段
readConcern: "available" 是唯一能解耦读与复制的手段
靠 readPreference 切流量解决不了 IO 高,因为业务读和 oplog 拉取仍共享同一套 I/O 调度器。只有 readConcern: "available" 能让业务读跳过 majority 提交等待,且完全不参与 oplog 应用路径。
- 必须在应用层显式指定:Node.js Driver 中不能依赖默认,要写
readConcern: { level: "available" } - 连接字符串里加参数无效,
&readConcernLevel=available只在 mongos 层解析,驱动必须透传 - 检查是否生效:在 Secondary 上执行
db.currentOp({ "readConcern.level": "available" }),应有活跃会话 - 注意兼容性:MongoDB
真正卡住的从来不是磁盘本身,而是没把「业务读」和「复制通道」在调度逻辑上分开。只要还在用 secondaryPreferred 却不配 readConcern: "available",IO 延迟就注定反复出现。


















