事务必须使用 primary 读取偏好,因为所有读写操作需路由至同一主节点以保证因果一致性和原子性;mongodb 强制覆盖任何非 primary 设置,驱动层忽略或报错,跨分片事务还要求各分片主节点均可用。

事务里不能设 secondary 或其他非 primary 读取偏好——这是硬性限制,不是配置问题。
为什么事务必须用 primary 读取偏好?
事务要求所有读写操作路由到同一个节点,且该节点必须是当前主节点(primary),否则无法保证因果一致性和原子性。MongoDB 在启动事务时会自动强制使用 primary 模式,任何在事务内显式调用 setReadPref() 或传入 readPreference 参数都会被忽略,甚至可能触发警告或报错。
-
primaryPreferred、secondary等模式在事务上下文中不合法,驱动层会拒绝或静默降级 - 即使你在连接字符串或客户端级别设了
readPreference=secondary,进入事务后它也完全失效 - 跨分片事务还额外要求所有参与分片的主节点都处于可用状态,进一步锁死路由路径
mongosh 中开启事务时的读取偏好行为
在 mongosh 里执行 session.startTransaction() 后,后续所有 session 内的操作(包括 find()、updateOne())自动绑定到当前 primary,无需、也不应手动设置 readPreference。
- 调用
db.getMongo().setReadPref('secondary')对事务内操作无效 -
session.getDatabase('db').getCollection('coll').find().readPref('secondary')会在事务中直接报错:"Read preference in a transaction must be primary" - 事务外的游标可以自由设
readPref,但一旦session.startTransaction()被调用,该 session 就脱离了全局读取偏好控制
客户端驱动(PHP / C / Node.js)里的实际写法
不同驱动对事务读取偏好的处理逻辑一致:事务选项里根本不提供 readPreference 字段,你传了也会被丢弃。重点是确保事务外的读操作和事务内的写操作不互相干扰。
- PHP 驱动中,
startTransaction()方法不接受readPreference参数;传入的options只支持readConcern、writeConcern和maxCommitTimeMS - C 驱动中,
mongoc_transaction_opts_set_read_concern()存在,但没有对应set_read_preference()函数 - Node.js 驱动同理,
startTransaction({ readConcern, writeConcern })—— 没有readPreference字段 - 如果业务需要“事务写 + 从节点读”,必须拆成两个独立 session:一个带事务的写 session(自动
primary),一个普通 session 显式设readPreference: 'secondary'
真正容易被忽略的点是:事务的隔离性依赖节点一致性,而不是你传了什么参数。哪怕你绕过驱动硬编码发命令,服务器端也会校验并拒绝非 primary 的读取偏好。别试图 hack,该让读写分离就拆 session,别塞进一个事务里。











