tags无法实现真正只读,因其仅影响客户端读请求分发,不阻止写命令到达节点或参与选举;真只读需组合hidden: true + priority: 0 + slavedelay + directconnection=true强制直连。

只读节点不能靠 tags 单独实现,它只是路由辅助手段;真要隔离写入、保障报表节点不被误写,必须组合 hidden: true + priority: 0 + slaveDelay + 客户端强制直连,tags 仅用于 readPreference 匹配,且前提是该节点没被设为 hidden。
为什么 tags 无法让节点真正只读
tags 是副本集配置里的键值对(如 {"role": "report"}),只影响客户端驱动的读请求分发逻辑。它不改变节点本身的读写能力,也不阻止写命令到达该节点——只要它还在 replica set 成员列表里、没被隐藏、且有投票权,就可能在故障转移中被选为 primary,或被 readPreference=primary 的应用误连后执行写操作。
常见错误现象:
- 给 secondary 节点加了
tags: {readonly: true},但没设hidden: true,结果运维用mongo --host rs0/...连上去,照样能db.collection.insert(...) - 应用配置
readPreference=secondaryPreferred&tagSets=[{"readonly":"true"}],但节点未设priority: 0,某次网络抖动后它被临时选为 primary,所有带 tag 的读请求瞬间变成写穿透
tags 配合 readPreference 的正确用法
当你要把「部分读流量」定向到特定 secondary(比如专用于 BI 查询的节点),且该节点仍参与副本集发现、可被选举,tags 才有意义。此时需两端配合:
- 节点配置中显式声明
tags,例如:rs.add({ host: "bi-node:27017", priority: 0.5, tags: { role: "bi", region: "cn-east" } }) - 客户端连接串指定
tagSets,如 Python PyMongo:MongoClient("mongodb://node1:27017,node2:27017/", read_preference=ReadPreference.SECONDARY, tag_sets=[{"role": "bi"}]) - 注意:如果多个节点匹配同一组 tag,驱动会从中随机选一个;若无节点匹配,则回退到默认 readPreference 行为(可能打到 primary)
想让报表节点彻底不接写请求?别依赖 tags
真实生产中,报表专用节点必须切断一切写入口,tags 在这里反而增加风险——因为它会让节点继续暴露在 replica set 发现机制下。正确做法是:
- 在
rs.add()时同时设置:hidden: true(不在成员列表中暴露)、priority: 0(无选举资格)、slaveDelay: 3600(延迟一小时,防误同步脏数据) - 该节点的
mongod.conf中禁用本地绕过:setParameter: { enableLocalhostAuthBypass: false } - 报表应用连接时,**必须用单节点 URI +
directConnection=true**,例如:mongodb://report-node:27017/mydb?directConnection=true
,否则驱动会尝试拉取 replica set 配置,发现节点 hidden 就跳过 - 绝对不要在该连接串里配
readPreference,因为 directConnection 模式下它无效;所有读操作天然只发生在这台机器上
最容易被忽略的一点:即使节点已 hidden+priority 0,如果应用连接串里写了 replica set 名(如 mongodb://node1,node2,node3/?replicaSet=rs0),驱动仍会尝试获取完整拓扑,一旦发现 hidden 节点,可能触发重试或静默降级——这不是 bug,是设计使然。所以「强制直连」不是可选项,是必要条件。











