transaction numbers不一致会导致跨分片事务失败,因各分片事务号须严格单调递增且全局对齐;若某分片缓存未及时刷新或同步延迟,会触发transactiontooold错误。

为什么 Transaction numbers 不一致会导致跨分片事务直接失败
MongoDB 4.2+ 的跨分片事务依赖所有参与分片的 transaction number 严格单调递增且全局对齐。一旦某个分片(尤其是 mongos 路由到的任意一个 shard)的 logicalSessionCacheRefreshPeriodMS 配置过大,或该分片长时间未与 config server 同步,它的事务计数器就可能落后——此时发起事务,mongos 会收到 InvalidOptions: transaction number is older than the latest seen 或类似 TransactionTooOld 错误。
- 检查每个分片的实时事务号:连接到各
shard直接执行db.runCommand({getLastError: 1}),观察返回中的txnNumber是否明显滞后 - 强制刷新逻辑会话缓存:在每个
shard上执行db.adminCommand({refreshLogicalSessionCacheNow: 1})(注意这不是原子操作,需逐个调用) - 生产环境更稳妥的做法是调小
logicalSessionCacheRefreshPeriodMS(默认 300000ms → 建议设为30000),并在滚动重启分片时确认该配置已生效
maxTimeMS 在跨分片事务里不是超时保险丝
你给 session.startTransaction({maxTimeMS: 5000}),不代表整个两阶段提交(2PC)会在 5 秒内回滚。这个参数只约束「主协调者(coordinator shard)上事务主操作的执行时间」,不包含 prepare 阶段广播、其他分片响应延迟、commit/abort 消息往返等网络和协调开销。实际事务卡住十几秒甚至分钟级后才报错,非常常见。
- 真实瓶颈常在 prepare 阶段:某个分片因锁争用或慢查询无法及时返回
prepare响应,整个事务就挂起,直到transactionLifetimeLimitSeconds(默认 60)触发自动 abort - 不要依赖
maxTimeMS做业务超时控制;应用层必须自己设外层定时器,在session.commitTransaction()或session.abortTransaction()调用前主动中断 - 开启
sh.setShardingBalancer( { waitForDelete: true } )类似操作时,务必避开事务高峰期——这类管理命令会短暂阻塞分片上的 prepare 流程
哪些写操作根本不能放进跨分片事务
MongoDB 明确禁止在跨分片事务中执行某些命令,不是性能问题,而是架构层面不可行。比如对 config 数据库的任何写入、system.* 集合操作、createCollection(即使目标库已分片)、以及所有涉及非分片集合(unsharded collection)的写操作——只要事务里混了哪怕一条,整个事务启动就会立刻失败,报 IllegalOperation: Cannot perform operation X in a multi-document transaction on a sharded cluster。
- 特别注意
$lookup:如果 pipeline 中引用了未分片集合,即使只是读,也会让事务被拒绝(4.4+ 更严格) -
explain()和countDocuments()在事务中可用,但estimatedDocumentCount()不行(它绕过事务快照) - 批量写入(
bulkWrite)必须确保所有操作目标都是同一分片键范围内的文档;跨多个分片键值的 bulk 写入,会被拆成多个子事务——而 MongoDB 不允许手动控制这种拆分,容易触发隐式限制
两阶段提交延迟高,但日志里看不到明显错误
事务 commit 成功返回后,数据其实在各分片上是「prepare 状态」,要等所有分片都确认 commit 才真正可见。这中间的延迟(通常几十到几百毫秒)不会抛错,但如果你紧接着用另一个 session 去查,可能读不到最新值——尤其当读取 session 没指定 readConcern: "snapshot" 时,大概率看到的是旧快照。
- 验证是否真延迟:在 commit 后立即用新 session 执行
db.collection.find().readConcern("majority"),对比"snapshot"结果;若后者能读到新值而前者不能,说明是预期的 2PC 提交传播延迟 - 别在事务 commit 后立刻发
change stream事件监听——event 的clusterTime可能早于实际 commit 时间,导致漏事件 - 监控关键指标:关注
shard上的transactions.prepared和transactions.committed计数差;差值持续 > 0 表示有事务卡在 prepare 阶段,需查对应分片的锁和慢日志
跨分片事务的“原子性”代价是协调复杂度,很多看似边缘的配置项(比如 transactionLifetimeLimitSeconds 和 sharding.chunkSize 的组合)会悄悄放大延迟。别只盯着代码里的 startTransaction,得去每个分片上盯住它的状态和时钟。










