事务内读操作天然为快照隔离,由wiredtiger在starttransaction()时自动提供一致性视图,无需且不可显式设置readconcern: "snapshot";事务外使用该readconcern需副本集或分片集群并启用majority写关注。

事务内读操作天然就是 snapshot 隔离,无需验证
只要在 session.startTransaction() 启动的上下文中执行 find、count、aggregate 等读操作,WiredTiger 就自动基于事务启动瞬间的全局快照提供一致性视图——这不是可配选项,而是引擎层强制行为。你不需要、也无法用 readConcern: "snapshot" 显式开启它,传了反而被忽略。
常见误解是试图在事务内加 readConcern: "snapshot" 来“启用”快照读,实际会静默失效;更危险的是把它和事务外的 readConcern: "snapshot" 混为一谈,后者依赖集群拓扑且语义不同。
验证事务外 readConcern: "snapshot" 是否生效
这个操作不是验证隔离级别,而是验证集群是否满足快照读前提条件。失败时通常报错:read concern 'snapshot' is not supported in standalone mode 或 Snapshot read concern requires majority write concern。
- 必须运行在副本集(含至少 3 节点)或分片集群,单节点
mongod直接拒绝 - 副本集必须启用
writeConcern: "majority"(即所有写操作默认等待多数节点确认) - 连接字符串需包含 replica set name,例如
mongodb://host1:27017,host2:27017/?replicaSet=rs0 - 操作必须通过
mongos(分片集群)或主节点(副本集)发起,不能发给从节点
验证命令示例:
db.runCommand({<br> find: "orders",<br> readConcern: { level: "snapshot" },<br> filter: { _id: ObjectId("...") }<br>})若返回数据且无报错,说明快照读已就绪;若报错,则按上述四点逐项检查。
用并发写入 + 事务内读来观察 snapshot 行为
真正能感知 snapshot 效果的,是对比「事务内读」和「事务外实时读」对同一数据的差异。典型验证场景:
- 开启事务 A,读取文档 X 的字段
balance(假设为 100) - 另起一个不带事务的连接,更新 X 的
balance为 200 并提交 - 回到事务 A 中再次
findX → 仍看到 100,而非 200 - 提交事务 A 后,再从外部读 → 才能看到 200(或其它新值)
注意:这个验证必须确保两次读都使用同一个 session 对象,且事务未提交;如果误用不同 session 或提前 endSession(),快照上下文丢失,结果不可靠。
容易被忽略的陷阱:session 生命周期和线程绑定
验证失败最常不是因为配置不对,而是 session 被错误复用或提前释放:
- HTTP handler 中把
client.StartSession()存成全局变量 → 多请求并发调用startTransaction()报错Transaction numbers do not match - 忘记在 defer 中调用
session.EndSession()→ 快照长期滞留,WiredTiger 版本链膨胀,后续查询变慢甚至触发TransactionTooLargeForCache - 跨 goroutine / thread 使用同一个 session 实例 → 时间戳错乱,可能读到其他事务的快照
正确模式永远是:每个业务逻辑单元(如一次 API 请求)独立创建 session,用完立刻 endSession(),绝不缓存、绝不共享。
快照读的代价藏在后台:事务越长,旧版本数据保留越久。哪怕只是验证,也别让事务空跑超过 5 秒。











