read_preference控制读请求路由节点,需用ReadPreference常量而非字符串;write_concern控制写确认级别,影响持久性与延迟;read_concern确保读已提交数据,依赖服务端配置且须显式传入操作。

read_preference 决定读请求发给哪个节点
读写关注(Read/Write Concern)不是单个开关,而是两套独立配置:读偏好(read_preference)控制从副本集的哪个节点读数据,写关注(write_concern)控制写操作成功需要满足什么条件。很多人误以为设置 read_preference 就能“强制读主库”,其实它只影响路由策略,不保证强一致性。
常见错误是直接传字符串,比如 read_preference="primary" —— 这会报错,因为 PyMongo 要求用预定义常量:
-
ReadPreference.PRIMARY:默认,读主节点(但不阻塞等待主节点最新数据) -
ReadPreference.SECONDARY:只读从节点,适合读多写少、容忍少量延迟的场景 -
ReadPreference.NEAREST:选网络延迟最低的节点,不管主从,适合地理分布广的部署
必须配合 pymongo 导入使用:
from pymongo import MongoClient, ReadPreference
client = MongoClient(
'mongodb://rs1:27017,rs2:27017,rs3:27017/?replicaSet=myRs',
read_preference=ReadPreference.SECONDARY
)
write_concern 控制写操作的确认级别
write_concern 直接影响数据持久性与响应延迟。设得太松(如 w=0)可能丢数据;设得太严(如 w="majority")在多数节点宕机时写会卡住或超时。
关键参数只有三个:
-
w:要求多少个节点确认写入,可为数字(w=2)、字符串(w="majority")或0(不等待确认) -
j:是否等待写入 journal(日志),j=True表示落盘才返回,j=False可能因崩溃丢数据 -
wtimeout:单位毫秒,w未满足时的等待上限,避免无限阻塞
推荐按场景选:
- 普通业务:用
write_concern={"w": "majority", "j": True} - 高吞吐日志类:用
write_concern={"w": 1, "j": False},牺牲一点可靠性换速度 - 本地开发测试:可设
write_concern={"w": 0},但上线前务必改掉
注意:不能全局设死,应在 collection 级或操作级动态覆盖:
coll = db.mycol.with_options(
write_concern=WriteConcern(w="majority", j=True)
)
coll.insert_one({"x": 1}) # 此次插入受该 write_concern 约束
read_concern 用于读取已提交的数据
read_concern 是解决“读未提交”问题的核心,但它只在副本集 + WiredTiger 引擎 + MongoDB ≥3.2 下生效。很多人开了 read_concern="majority" 却没效果,往往是因为服务端没开启 majority read concern 支持(需在启动参数加 --enableMajorityReadConcern 或配置文件设 enableMajorityReadConcern: true)。
两个常用值:
-
"local":默认,读本节点最新数据,可能包含后续被回滚的写入 -
"majority":只读已被大多数节点确认、不会回滚的数据,真正实现“已提交即可见”
必须显式传入操作中,不能只设 client 级:
coll.find({"x": 1}, read_concern=ReadConcern("majority"))
注意:read_concern 和 read_preference 可组合使用,比如 read_preference=SECONDARY + read_concern="majority",表示从某个从节点读,但该节点必须已同步 majority 确认过的数据。
连接字符串里不能设 read/write concern
有人试图在 URI 里写 mongodb://host/?readPreference=secondary&writeConcern=w:majority —— 这在 PyMongo 4.0+ 里会被忽略。URI 只解析基础连接参数(host、port、replicaSet、auth 等),所有 read_preference、write_concern、read_concern 都必须在 Python 代码中显式传参。
容易踩的坑:
- 把
write_concern传成字典却漏了WriteConcern类包装,导致部分操作不生效 - 在
MongoClient初始化时设了read_preference,但后续对某个 collection 调用find()时又传了不同值,后者会覆盖前者 - 用
insert_many批量写入时,整个批次共用一个write_concern,无法为每条文档单独设
真正复杂的点在于三者组合后的语义差异:比如 read_preference=PRIMARY + read_concern="majority" 并不等于“强一致读”,它仍可能读到主节点上尚未传播到多数节点的数据——只有 read_concern="majority" 本身才提供这个保证,且依赖服务端配置和引擎支持。











