rs.secondaryOk()仅允许当前shell会话读Secondary,不自动路由读请求;必须显式配置readPreference(如secondary)并指定replicaSet参数,同时确保Secondary节点priority=0、hidden=false且oplog延迟可控。

rs.secondaryOk() 不等于「自动路由只读请求」
很多用户以为在应用里连上 Secondary 节点后执行 rs.secondaryOk(),就能让所有查询自动走从节点——这是错的。这个命令只是告诉当前连接「允许读取 Secondary」,但 MongoDB 驱动默认仍会把读请求发给 Primary,除非你显式指定读偏好(read preference)。
常见错误现象:应用连的是 Secondary 地址,rs.secondaryOk() 也执行了,但 db.collection.find() 仍报 not master 或直接超时;或者监控发现所有读流量仍在 Primary 上。
- 必须在连接字符串中加
?readPreference=secondary(或secondaryPreferred),驱动才按规则分发读请求 - Node.js 驱动里不能只靠
rs.secondaryOk(),得配readPreference: 'secondary'选项 - Java 驱动需设置
ReadPreference.secondary(),不是靠 shell 命令生效 -
rs.secondaryOk()的作用范围仅限于当前 shell 会话,重启 mongo shell 就失效
Secondary 节点必须开启 priority=0 + hidden=false
想让某个 Secondary 稳定承担报表查询,又不希望它被选为 Primary,就得在副本集配置里把它设为「不可参选、可见、只读」。priority=0 是关键,否则选举时它可能意外升主,导致报表服务突然写入失败或数据延迟突增。
容易踩的坑:直接改 rs.conf() 里某个成员的 priority 后没调用 rs.reconfig(),或者忘了加 {force: true}(当有节点不可达时必需)。
- 先查当前配置:
rs.conf().members[1](假设是第二个成员) - 更新配置(示例):
cfg = rs.conf(); cfg.members[1].priority = 0; cfg.members[1].hidden = false; rs.reconfig(cfg, {force: true}) - 确认生效:
rs.status().members[1].stateStr应为SECONDARY,且rs.conf().members[1].priority === 0 - hidden=true 会让该节点对客户端不可见,报表应用根本连不上,别误配
报表查询前务必检查 oplog 同步延迟
Secondary 节点的数据不是实时的,尤其报表类长查询容易卡住复制线程,导致 oplog lag 持续升高。一旦 lag 超过 oplog 容量(比如 30 分钟),该节点就会停止同步,变成「stale」状态,后续查询返回陈旧数据甚至报错 node is stale。
真实场景中,凌晨跑大报表常触发这个问题,但日志里只有模糊的 replSetSyncFrom 错误,不容易定位。
- 监控命令:
rs.printSlaveReplicationInfo()或看rs.status().members[x].optimeDate与 Primary 的差值 - oplog 大小建议:至少保留 2–3 小时写入量,用
db.getSiblingDB('local').oplog.rs.stats()查当前容量 - 如果报表查询太重,考虑加
maxTimeMS限制单次查询时长,避免阻塞复制 - 不要依赖
slaveOk()后就不管同步状态——它不解决延迟问题
连接字符串里别漏掉 replicaSet 参数
即使你只连一个 Secondary 节点地址,也必须带 ?replicaSet=rs0。否则驱动不会启用副本集逻辑,无法感知节点状态变化,更不会自动故障转移或按 readPreference 路由。
典型错误:报表脚本里写的是 mongodb://10.0.1.10:27018/mydb,没加 replicaSet,结果某天 Secondary 切换后连接直接断开,脚本静默失败。
- 正确写法:
mongodb://10.0.1.10:27018,10.0.1.11:27018,10.0.1.12:27018/mydb?replicaSet=rs0&readPreference=secondary - 即使只填一个地址,也要加
replicaSet,否则驱动降级为单机模式 - Python PyMongo 里若用
MongoClient(hosts, replicaSet='rs0'),hosts 仍需包含全部节点 IP,不然发现不了拓扑变更
最易被忽略的一点:Secondary 节点的 readConcern 默认是 local,报表查到的数据可能还没进 majority commit,遇到主节点宕机就丢失。真要强一致性报表,得显式设 readConcern=majority,但会增加延迟和失败概率——得权衡。











