MongoDB设rs.secondaryOk后secondary节点执行脚本报NotWritablePrimary
问题产生原因
- 核心认知偏差:
rs.secondaryOk()的作用仅为放开副本集secondary节点的读操作限制。默认配置下secondary节点不接受直接读请求,配置该参数后可以在secondary读取数据,但它完全不会给secondary开放写权限。MongoDB副本集的常规写操作仅能在primary节点执行,secondary节点始终处于用户只读状态,直接在secondary执行remove这类写命令必然返回NotWritablePrimary错误。 - 脚本语法不兼容非交互场景:你脚本里写的
use foo是mongo shell的交互式专用命令,通过stdin重定向(即你用< /path/to/script.js的传参方式)执行脚本时,这类交互式命令无法被正确解析,会导致数据库上下文切换失败,不过这个问题不是触发本次报错的核心原因。 - 连接逻辑缺陷:你当前的连接命令是直连单个节点,如果直连的恰好是secondary节点,对应版本的mongo shell不会自动把写请求转发到primary,直接发起写操作就会触发报错。
解决方法
- 调整连接逻辑,不要直连secondary执行写操作:所有增删改写请求必须发送到副本集primary节点,推荐在连接时填写副本集全节点地址和副本集名称,让mongo客户端自动识别primary节点转发写请求,执行命令参考如下,替换对应占位符即可:
mongo "mongodb://<user>:<password>@<节点1IP>:27017,<节点2IP>:27017,<节点3IP>:27017/foo?replicaSet=<你的副本集名称>&authSource=admin" /path/to/script.js
注意:如果你的账号是在admin库创建的,必须带上
authSource=admin参数,否则会认证失败。
- 修正脚本语法适配非交互式执行:去掉无效的
rs.secondaryOk()配置和交互式use命令,改用脚本兼容的写法切换数据库,修正后的脚本内容:
// 切换到foo数据库,等价于交互式场景下的use foo const targetDb = db.getSiblingDB('foo'); targetDb.getCollection('users').remove({accountId: 123456});
- 可选增加节点校验逻辑:如果需要避免连错节点的问题,可以在脚本开头增加角色判断,非primary节点直接终止执行,避免无意义的报错:
const nodeRole = db.isMaster(); if (!nodeRole.ismaster) { print("当前连接节点不是primary,请使用副本集连接串执行,或直接连接primary节点"); quit(1); }
严禁通过修改副本集配置、强制提升节点角色等方式在secondary节点直接执行写操作,该行为会破坏副本集数据一致性,触发数据不同步、脑裂等严重生产故障。
内容的提问来源于stack exchange,提问作者Lewis Tran
相关产品推荐
相关产品推荐

