只读事务必须显式设置 read_concern={"level": "majority"},否则可能读到后续因主节点宕机而丢失的未确认数据;该配置要求写操作也使用 w="majority",且仅在副本集+wiredtiger+mongodb≥3.2下生效。

只读事务必须显式设置 read_concern={"level": "majority"},否则无法保证读到已提交且不会回滚的数据。
为什么 read_concern="majority" 是只读事务的硬性前提
只读事务不写数据,但它的语义是“读取一个稳定、已提交的快照”。MongoDB 的 read_concern="majority" 表示:只返回那些已被大多数副本集节点确认(即写入成功并落盘)的数据。如果没设这个,哪怕你开了事务,read_concern 默认是 "local" —— 也就是可能读到尚未复制出去、后续因主节点宕机而丢失的“幽灵数据”。
常见错误现象:session.startTransaction() 后直接 find(),结果在主节点故障切换后发现刚读到的订单记录消失了。
- 该配置仅在副本集(非单机)+ WiredTiger 引擎 + MongoDB ≥ 3.2 下生效
- 它和
read_preference无关——即使你设了ReadPreference.PRIMARY,也不代表能读到已提交数据 - 不能靠连接串或客户端全局默认值,必须在
startTransaction()时传入
read_concern 在事务中必须与 write_concern 协同
只读事务虽不写,但它依赖整个集群的写确认状态。MongoDB 要求:只有当写操作本身用了 w="majority",read_concern="majority" 才有意义。否则,“多数已确认”这个视图根本不存在。
例如:某条订单插入只用了 {w: 1},那即使你事务里设了 read_concern="majority",它也只能等一个空集合——因为没有数据满足“被多数节点确认”这一条件。
- 检查副本集健康状态:
rs.status().members中所有votes: 1节点都应为stateStr: "PRIMARY"或"SECONDARY" - 分片集群下,
read_concern="majority"需要每个 shard 都满足 majority 写确认,且 config server 正常 - 如果写操作历史中混用了
w=1,只读事务即使设了majority也退化为local级别(无报错,静默降级)
如何正确启动只读事务并验证配置生效
PyMongo 示例(其他驱动逻辑一致):
session = client.start_session()
session.start_transaction(
read_concern={"level": "majority"},
read_preference=ReadPreference.PRIMARY # 路由到主节点,但不改变一致性级别
)
try:
result = collection.find_one({"_id": order_id}, session=session)
# 此次读取受 majority read_concern 约束
finally:
session.end_session()
关键点:
-
read_concern必须作为start_transaction()的参数传入,不能在 collection 或 query 级设置 -
read_preference可同时指定,但它只决定发给谁,不影响数据可见性边界 - 执行后可通过
db.currentOp({secs_running: {$gt: 0}})查看事务是否带"readConcern": {"level": "majority"}
最容易被忽略的是:事务开启后不做任何写操作,不代表它就“轻量”——read_concern="majority" 仍会触发对多数节点的元数据协调,延迟比普通读高;若副本集节点间网络延迟大或有节点短暂失联,查询可能卡在等待多数确认上,wtimeout 对读无效,只能靠客户端超时控制。











