MongoDB 4.0+事务必须运行在副本集或分片集群上,不支持单节点;需配置replSet并initiate单节点副本集,事务中须显式创建session、设置readConcern:"snapshot"和writeConcern:{w:"majority"},超时默认60秒且不可跨库操作。

事务在 MongoDB 4.0+ 中必须运行在副本集或分片集群上
MongoDB 4.0 引入的多文档事务不支持单节点(standalone)部署,这是硬性限制。如果你直接在本地 mongod --port 27017 启动,执行 session.startTransaction() 会立刻报错:Transaction numbers are only allowed on a replica set member or mongos。必须先将单节点升级为副本集,哪怕只有一台机器也要模拟副本集结构。
初始化单节点副本集的最小可行配置
不需要额外机器,只需修改启动参数并执行初始化命令。关键点在于:所有节点(哪怕只有一个)必须使用相同的 replSetName,且绑定地址要允许本地连接。
- 启动时加参数:
mongod --replSet rs0 --bind_ip localhost,127.0.0.1 --port 27017 - 连进 shell 执行:
rs.initiate({ _id: "rs0", members: [{ _id: 0, host: "localhost:27017" }] }) - 等待状态变为
PRIMARY(用rs.status().myState查)再开始事务
注意:如果之前有数据,initiate 后原数据库仍可用,但集合自动变为可事务(无需额外开启开关)。
事务代码中必须显式创建会话并指定读写关注
即使副本集就绪,事务也不会自动生效。每个事务块必须绑定到一个会话(session),且该会话需在事务内显式调用 startTransaction()。常见漏掉的点是读写关注(readConcern 和 writeConcern)未设——默认值可能不满足事务一致性要求。
-
readConcern: "snapshot"是事务内读操作的强制要求,否则报read concern is not valid for this command -
writeConcern: { w: "majority" }推荐设置,避免主节点宕机后未同步的事务回滚 - 示例片段:
const session = client.startSession(); await session.withTransaction(async () => { const coll = db.collection("orders"); await coll.updateOne({ _id: 1 }, { $inc: { balance: -100 } }, { session }); await coll.updateOne({ _id: 2 }, { $inc: { balance: 100 } }, { session }); });
事务超时与长运行事务的隐性风险
MongoDB 默认事务最长 60 秒(transactionLifetimeLimitSeconds),超时自动 abort。但更隐蔽的问题是:事务期间若发生主从切换,新 Primary 可能没有该事务上下文,导致后续操作失败或返回陈旧数据。
- 可通过
db.adminCommand({ setParameter: 1, transactionLifetimeLimitSeconds: 300 })调整,但不建议盲目延长 - 避免在事务中做 HTTP 请求、文件 IO 或用户交互等不确定耗时操作
- 事务内不要跨数据库操作(
db1.coll1和db2.coll2不能同属一个事务)
真正难调试的是事务和非事务操作混用时的隔离问题:比如事务中读取了某文档,事务外另一个进程立刻更新它,事务内再次读取可能看到不同快照——这不是 bug,是 snapshot 隔离级别的正常行为,得靠业务逻辑兜底。











