事务内查不到刚写的数据,是因为未设置 readConcern: "snapshot";local 和 available 级别不保证看到本事务未提交的变更,只有 majority 或 snapshot 级别才能保障事务内可见性。

事务中查不到自己刚写的数据,不是代码写错了,而是读关注(read concern)没设对——local 和 available 会跳过未提交事务的变更,只有 majority 或显式指定 readConcern: { level: "snapshot" } 才能保证事务内可见性。
事务内查询看不到刚写入的数据?检查 readConcern 配置
MongoDB 事务默认不自动提升读关注级别。即使在同一个 session 中执行 insertOne 后立刻 findOne,若未显式设置 readConcern,驱动可能仍按连接默认值(通常是 local)发起查询,而 local 读关注允许读取尚未提交、甚至可能回滚的 oplog 条目,但**不保证看到当前事务内未提交的变更**。
-
readConcern: "local":只保证读到已写入磁盘的文档,但不保证是事务内最新状态;事务未提交时,该读可能完全“看不见”本事务的写 -
readConcern: "majority":确保读到已确认写入大多数节点的数据,事务提交前不可见;但事务内部读需配合snapshot级别 -
readConcern: { level: "snapshot" }:事务内必须启用,才能让多次读操作看到一致的快照视图,包括本事务已写但未提交的数据
分片集群下 snapshot 读关注有额外限制
在分片集群中启用 snapshot 读关注,要求所有参与分片都支持该特性(4.2+),且事务必须路由到同一 mongos 实例——否则 mongos 可能将读请求发往未执行写操作的 shard,导致查不到数据。
- 错误现象:
ReadConcernMajorityNotAvailableYet或空结果,但explain()显示命中索引 - 根本原因:目标 shard 时间比
mongos慢 >300ms,拒绝处理带snapshot的请求(触发 clock_skew 校验失败) - 验证方式:在各节点运行
db.runCommand({isMaster: 1}),比对返回的localTime字段差值
事务外读取可能“部分看到”已提交事务
即使事务已调用 commitTransaction(),外部客户端用 readConcern: "local" 仍可能出现读偏差:写入在 shard A 已可见,但在 shard B 尚未回放完成,此时读请求落到 A 就能看到部分结果,落到 B 就看不到——这不是 bug,而是 local 级别的设计行为。
- 避免方法:对外部一致性要求高的查询,强制使用
readConcern: "majority" - 注意:这会增加延迟,因为要等待多数节点确认;若某 shard 落后严重,查询可能超时
- 开发调试时容易忽略:用
mongoshell 直连单个 shard 测试,误以为“数据已存在”,但生产中mongos路由行为完全不同
clock_skew 和 readConcern 级别是两个独立但常同时出问题的维度:前者让事务卡住或拒绝提交,后者让事务内或事务间读取行为不符合直觉。调事务问题时,先确认时间同步状态,再检查每条查询是否明确携带了符合语义的 readConcern。











