读写分离不会自动发生,必须在驱动层显式设置readPreference,否则所有读请求默认走主节点;失效主因是驱动未真正应用该策略,而非副本集配置问题。

读写分离不会自动发生,readPreference 必须在驱动层显式设置,否则所有读请求默认走主节点——哪怕你有 5 个空闲的 secondary。
为什么 readPreference=secondary 总是没生效?
根本原因不是副本集没配好,而是驱动没真正应用该策略。常见失效现象包括:
- 监控看到从节点 CPU/网络空闲,但
mongostat显示 query 全在主节点 - 连接字符串里写了
&readPreference=secondary,但代码中直接调用collection.find(),Node.js 驱动会忽略 URI 中的全局设置 - 设了
readPreference=secondary却报NotReadablePrimary:驱动发现没有可用secondary(比如全部延迟超限、状态非SECONDARY或optimeDate差距过大),按设计直接失败,不降级
Node.js 驱动中怎么设才真正起作用?
优先级顺序是:查询级 > 数据库级 > 客户端级 > 连接字符串。靠单一配置容易被覆盖。推荐组合使用:
- 连接字符串加基础验证参数:
mongodb://host1:27017,host2:27017/db?replicaSet=rs0&readPreference=secondary&maxStalenessSeconds=30 - 客户端初始化时设默认值:
new MongoClient(uri, { readPreference: 'secondaryPreferred' })(比secondary更稳妥) - 关键查询单独指定:
collection.find({ status: 'done' }).readPreference('secondary')(Node.js 4.0+ 写法;旧版用.withReadPreference('secondary')) - 强一致性读必须绕过从节点:
collection.findOne({ _id }, { readPreference: 'primary' })
maxStalenessSeconds 很关键:它限制从节点数据最多比主节点旧多少秒,默认是 -1(不限),设成 30 意味着延迟超 30 秒的 secondary 不会被选中。跨时区部署时若不设,可能读到几小时以前的数据。
GridFS 场景下 readPreference 的特殊行为
readPreference: "secondary" 仅影响 fs.files 元数据查询,openDownloadStream() 拉取 fs.chunks 时官方驱动仍默认走 primary,因为从节点缺少 {files_id: 1, n: 1} 索引,驱动为避免查询失败自动降级。
- 在所有从节点上手动创建索引:
db.fs.chunks.createIndex({ files_id: 1, n: 1 }, { readPreference: "primary" }) - 元数据查询显式走从节点:
bucket.find(..., { readPreference: "secondary" }),并加maxTimeMS: 2000防卡死 - chunk 读取改用原生 collection 查询:
db.fs.chunks.find({ files_id: xxx, n: { $gte: a, $lte: b } }, { readPreference: "secondary" }),并自行拼接Buffer
只读报表节点必须配合 directConnection=true
光把节点设为 hidden: true + priority: 0 + slaveDelay 不够——readPreference=secondary 不会选中 hidden 节点。必须强制直连:
- Node.js:
new MongoClient("mongodb://report-node:27017", { directConnection: true }) - Java:
directConnection=true+ 单节点 URI,禁用 replica set discovery - Python PyMongo:
MongoClient("mongodb://report-node:27017", directConnection=True)
否则驱动会尝试拉取 replica set 配置,发现节点 hidden 就跳过,白配。


















