错误“read concern level 'snapshot' is not supported”表明mongodb处于standalone模式,而snapshot读必须运行在副本集上;需通过--replset启动并执行rs.initiate()初始化副本集,且withtransaction()第二参数必须同时指定readconcern: {level: "snapshot"}和writeconcern: {w: "majority"}。

事务报错“Read concern level 'snapshot' is not supported”:先确认是否在副本集上运行
这个错误不是配置写错了,是 MongoDB 服务端直接拒绝——你正在 standalone 模式下尝试用 readConcern: "snapshot",而该级别硬性依赖副本集的 oplog 机制。单节点 mongod 启动时没带 --replSet、也没执行 rs.initiate(),就不可能支持 snapshot 读。
验证方式很简单:rs.status() 返回空对象或报 no replset config,就是 standalone。别折腾 enableMajorityReadConcern 或 journal 参数,它们不解决准入问题。
- 本地开发快速修复:停掉当前 mongod,用
mongod --replSet rs0 --dbpath /data/rs0 --port 27017重启,再连进去执行rs.initiate({ _id: "rs0", members: [{ _id: 0, host: "localhost:27017" }] }) - Docker 用户注意:
docker run必须加--replSet rs0,且容器启动后要进 shell 手动跑rs.initiate(),光靠环境变量或 entrypoint 脚本不保险 - 初始化后等
rs.status().stateStr === "PRIMARY"再试事务,否则仍会失败
withTransaction() 的 readConcern 必须传第二参数,不是塞进 findOne() options
事务内读不到刚写入的数据,90% 是因为把 readConcern 错放在了 findOne() 或 insertOne() 的 options 里。MongoDB 完全忽略这些位置的 readConcern,仍用默认的 "local",导致事务内读写不一致。
正确入口只有一个:session.withTransaction() 的第二个参数对象。所有事务内读操作(find、countDocuments、aggregate)自动继承它,无需重复设。
- 错误写法:
await collection.findOne({ x: 1 }, { readConcern: { level: "snapshot" } }) - 正确写法:
await session.withTransaction(async () => { ... }, { readConcern: { level: "snapshot" } }) - Mongoose 用户注意:
readConcern在 v6.0+ 才真正生效;v5.x 即使写了也静默失效
单节点副本集启用后,writeConcern 也必须配对设为 majority
副本集建好了,不代表事务就能 commit。MongoDB 强制要求事务内写操作必须搭配 writeConcern: { w: "majority" },否则抛出 Transaction numbers require a majority write concern。
这个 w: "majority" 不是可选优化项,是协议层硬性校验。哪怕你只启了一个成员的副本集(rs.initiate() 后只有一个 PRIMARY),它也必须满足 majority 条件——单成员时 majority 就是 1,所以能过;但如果你漏写,或者写了 { w: 1 },照样失败。
- 必须和
readConcern一起传进withTransaction()第二个参数,例如:{ readConcern: { level: "snapshot" }, writeConcern: { w: "majority" } } - 不要试图用
collection.insertOne(..., { writeConcern: { w: "majority" } })单独设——驱动不认,事务照样卡在 prepare 阶段 - 分片集群下,所有 shard 和 config server 都要统一开启
enableMajorityReadConcern: true,否则跨分片事务会在 prepare 时静默失败
“Snapshot reads require a replica set” 报错时,别碰 local/majority 级别来绕过
看到这个报错,说明你还没越过副本集门槛。此时改用 readConcern: "majority" 并不能解决问题——它同样要求副本集已启用 enableMajorityReadConcern,而 standalone 模式下这个开关根本不起作用。
强行降级到 "majority" 只会让错误变成更隐蔽的 ReadConcernMajorityNotAvailableYet 或事务提交超时,因为底层没有 oplog 可查。真正的分界点只有一个:是否完成 rs.initiate() 并进入 PRIMARY 状态。
容易被忽略的是主机名解析问题:rs.initiate() 里写的 host: "localhost:27017" 必须和驱动连接字符串里的 host 完全一致。用 127.0.0.1 或 mongodb://127.0.0.1:27017 连接,但 rs.initiate() 写了 "localhost",会导致驱动无法识别自己属于该副本集,session 创建失败。











