事务失败后的回滚操作会阻塞oplog同步,导致复制延迟激增;mongodb 6.0通过同步写入removed.*.bson文件实现回滚,若rollbackdir磁盘i/o拥堵(如与dbpath共用hdd、ext4文件系统、空间不足或barrier开销),将卡住整个复制流。

事务本身不会直接导致复制延迟激增,但事务失败后触发的回滚(rollback)会严重阻塞 oplog 同步,这才是复制延迟飙升的真正原因。
为什么 rollback 会让 secondary 落后几十秒甚至几分钟
MongoDB 6.0 的事务回滚不是靠 undo log 回退内存状态,而是同步写入 removed.*.bson 文件到 rollbackDir 目录。这个过程是阻塞式的:主节点降级或事务异常中断后,mongod 进程必须等所有 rollback 文件刷盘完成,才能继续追 oplog。若 rollbackDir 所在磁盘 I/O 拥堵(比如和 dbPath 共用一块 HDD、文件系统是 ext4、空间不足或有 barrier 开销),就会卡住整个复制流。
典型现象:top 看到 mongod 大量 D 状态进程;mongod.log 日志停在 Writing rollback data for collection 后无后续;rs.printSecondaryReplicationInfo() 显示 optimeDate 滞后时间持续扩大。
- 务必检查
rollbackDir是否与dbPath分离 —— 这是 6.0+ 强制要求,不能复用同一路径 - 确认该目录挂载在独立 SSD 上,且文件系统为 XFS(ext4 在大量小文件写入时易锁瓶颈)
- 禁用 journal 写入 rollback 分区:挂载参数加
barrier=0,data=writeback(仅限 XFS) - 不要依赖
transactionStats命令:它需要 internal 角色权限,生产环境基本不可用
如何提前发现回滚风险,而不是等它发生
等日志里出现 rollback file 已经晚了。你应该监控两个更早、更稳定的信号:
- 盯紧
serverStatus.metrics.transaction.currentInactive:该值 > 5 并持续 5 分钟不归零,说明有事务卡在 prepare 阶段,极大概率即将触发回滚 - 定期采样 oplog 时间戳差:
db.oplog.rs.find().sort({$natural:-1}).limit(1).next().ts.getTimestamp() - db.oplog.rs.find().sort({$natural:1}).limit(1).next().ts.getTimestamp(),若该差值在故障前 2 分钟内突然放大(比如从 10s 跳到 80s),就是回滚即将开始的征兆 - 主动 kill 长事务:
db.currentOp({secs_running: {$gt: 30}}).forEach(op => db.killOp(op.opid)),比等它超时再回滚更可控
分片集群中复制延迟排查要特别注意什么
分片集群不是“全集群一起回滚”,而是按分片粒度独立判断。你必须逐个检查每个 shard 的状态:
- 连接到每个
mongos,执行sh.status()查看哪些 shard 曾短暂成为临时 primary - 登录对应 shard 的 primary 节点,运行
db.runCommand({getCmdLineOpts: 1})确认其rollbackDir配置是否生效且路径可写 - 检查该 shard 的日志是否有
createrollbackdatafiles关键字 —— 只有真正生成了 rollback 文件的分片,才会拖慢整个集群的复制进度 - 别只看 config server 或 mongos 的延迟指标:它们不反映单个 shard 的 rollback 卡顿
真正难处理的从来不是“怎么查延迟”,而是“怎么让 rollback 不发生”。把事务控制在 10 秒内、避免跨分片事务、不在事务里做聚合或大范围更新——这些设计约束,比任何监控脚本都管用。











