跨分片事务本质是二阶段提交实现强制一致,非自动加速;需启用分片集群、所有集合已分片、各分片为副本集、客户端显式传入会话,并合理设计分片键以减少跨分片操作。

跨分片事务靠二阶段提交,不是“自动变快”,而是“强制一致”
MongoDB 4.2+ 的跨分片事务本质是协调器(mongos)发起的二阶段提交(2PC),不是把单分片事务简单复制到多个分片。它会先让所有涉及分片预写日志、预留资源(prepare 阶段),再统一确认提交或中止(commit/abort 阶段)。这意味着:即使网络抖动、某个分片短暂不可用,事务也不会“半成功”。
- 必须启用
sharding集群,且所有目标集合已分片(shardCollection已执行),否则事务直接报错CommandNotSupportedOnShardedCluster - 每个分片必须是副本集(非单节点),否则
startTransaction()会失败并提示TransactionNumbers are not supported on standalone servers - 客户端驱动必须显式传入会话(
session),所有操作(insertOne、updateMany等)都得带这个 session 对象,漏一个就脱离事务上下文
session.startTransaction() 前必须检查分片键设计
跨分片事务性能损耗主要来自分片间协调开销——协调越多分片,延迟越高、超时风险越大。而分片键是否能让相关数据“尽量落在同一分片”,直接决定你是不是真需要跨分片事务。
- 例如:订单和订单项用
orderId作分片键,并将二者存于同一数据库、用相同分片键哈希路由,那绝大多数读写其实压根不跨分片 - 反例:用户表按
userId分片,但订单表按createdAt分片,一笔下单+扣余额+记日志的操作必然横跨多个分片,事务延迟可能从 20ms 拉到 300ms+ - 注意:
transactionLifetimeLimitSeconds默认是 60,大事务容易触发TransactionTooOld;若真需长流程,得在mongos配置里调高,不能只改客户端
Node.js 和 C# 驱动里最容易漏掉的三件事
驱动封装了 API,但底层约束不会消失。很多“事务没生效”问题,其实是驱动用法没对齐服务端要求。
- Node.js(v6.3+ 驱动):必须用
withTransaction()辅助函数,或手动session.startTransaction()+try/catch/finally,**不能只调startTransaction()就不管了**——没commitTransaction()或abortTransaction(),会话卡住、连接泄漏 - C#(.NET Driver >= 2.7):
session.Client.GetDatabase()返回的 database 必须再通过session.WithTransaction()包裹,否则集合操作不走事务上下文 - 所有语言:事务内禁止执行
createCollection、dropDatabase等 DDL,连insert触发的隐式建集合都不行,否则报CommandNotSupportedInTransaction
别拿分布式事务当“万能锁”,先问自己要不要
MongoDB 的单文档原子性覆盖了大多数场景。真要跨分片事务,往往说明数据模型没对齐访问模式——比如把强关联的业务实体硬拆到不同分片键上。
- 查一下慢查询日志:如果
explain("executionStats")显示executionStages.shards里只有一两个分片参与,那大概率不需要跨分片事务,优化分片键或聚合逻辑更治本 - 事务内避免大数组更新、全量
$set文档、或遍历游标后再写回——这些操作会让事务内存占用飙升,触发ExceededTimeLimit或 OOM kill - 生产环境首次上线跨分片事务前,务必在影子集群压测:模拟网络分区、随机 kill 一个分片的 primary,看事务是否真的回滚干净、会不会残留 prepare 状态文档
跨分片事务不是开关一开就稳,它把一致性保障交给了协调器,也把延迟和复杂度一起接了过来。用之前,先看看你的分片键是不是在帮你,而不是拖后腿。










