分片集群中事务无法使用,根本原因是事务操作被路由到多个分片触发IllegalOp错误;所有读写必须落在同一分片,要求显式包含完整分片键,禁止访问未分片集合,maxTimeMS在分片事务中无效。

分片集群里事务“无法使用”,根本不是版本不够或配置没开,而是事务操作被路由到多个分片上,触发了 IllegalOp 错误 —— MongoDB 会直接拒绝启动事务,连 startTransaction 都不会成功。
事务内查询没带全分片键,mongos 就不知道往哪发
分片集群要求事务中所有读写操作必须落在同一分片上,否则视为非法。如果你在事务里执行 db.orders.find({ status: "pending" }) 这类不含分片键的查询,mongos 无法定位目标分片,立刻报错 IllegalOp。
- 检查方式:用
sh.findShard("db.orders", { _id: ObjectId("...") })确认关键文档实际落在哪个分片 - 所有事务内操作必须显式包含完整分片键,例如
{ tenant_id: "abc", order_id: 123 },且字段类型、值存在性要和文档一致 - 复合分片键只传前缀(如
{ a: 1 }却不传b)会导致范围不可定界,同样被拒 - 哈希分片键支持
$eq,但$in、$gt等仍可能跨分片,必须实测验证
事务里混进了未分片集合,整个事务当场终止
只要事务中出现对未启用分片的集合(包括 config、local、system.*,或虽在分片集群但没执行过 sh.shardCollection 的集合)的写操作,MongoDB 在解析阶段就拦截,返回 IllegalOp。
- 典型场景:日志写入临时表、审计记录插进非分片库、误调
db.runCommand({ setParameter: 1 }) - 用
db.getSiblingDB("db_name").getCollectionNames()查每个集合是否含shardKey字段 - 代码里避免硬编码库名;改用
database.getCollection("coll")并提前断言coll.stats().sharded === true - ORM 或配置中心若自动建集合,务必关掉
autoCreate,由 DBA 统一预分片
maxTimeMS 在分片事务里根本不起作用
给 session.startTransaction({ maxTimeMS: 5000 }) 设值,不代表事务会在 5 秒内结束。它只限制协调分片上的主操作耗时,不控制跨分片通信、网络延迟或从分片响应时间。
- 真正起效的是全局参数
transactionLifetimeLimitSeconds(默认 60 秒),需在所有分片副本集成员上统一设置 - 超时后事务会被后台清理进程强制中止,状态变为
TransactionTooOld - 别指望靠
maxTimeMS挡住慢查询或网络抖动,得靠前置校验 + 分片键收敛 + 操作精简
最容易被忽略的点是:事务失败往往发生在 startTransaction 调用那一刻,而不是后续某条 insertOne —— 错误已经暴露在路由阶段,但日志里可能只显示模糊的 IllegalOp,不提示具体哪条语句违规。查 sh.status() 和逐条验证分片键,比加日志更有用。

















