spring boot中mongodb读写分离的正确方式是仅配置一个副本集uri并设置readpreference参数,而非多个uri或多mongotemplate;必须包含全部节点地址、replicaset名称匹配且大小写敏感,readpreference设为secondary或secondarypreferred以控制读流量走向从节点,写操作始终由主节点处理。

Spring Boot 中 MongoDB 的读写分离,本质不是靠配多个 uri 或多个 MongoTemplate 实现的——那是伪分离,容易绕过副本集语义、破坏一致性,也扛不住故障转移。真正可行且生产推荐的方式,是**只配一个副本集 URI + 正确设置 readPreference**。
用副本集 URI 替代单节点地址
很多人卡在第一步:连接串写成 mongodb://192.168.68.137:27017 这种单点格式,MongoDB 驱动根本感知不到从节点,readPreference 也就完全失效。
- 必须把所有节点(主+从)写进同一个
uri,格式为:mongodb://192.168.68.137:27017,192.168.68.138:27017,192.168.68.139:27017/?replicaSet=rs0 -
replicaSet参数名必须和 MongoDB 实际初始化时用的_id完全一致(大小写敏感),否则驱动连不上任何节点 - 不要在
application.yml里拆开写host/port,Spring Boot 的spring.data.mongodb.uri是唯一受支持的入口,其他字段(如host)会覆盖掉 URI 中的节点列表
通过 readPreference 控制读流量走向
副本集内读操作路由由客户端驱动控制,不是靠 Spring Boot 自动分发。关键就在 readPreference 参数,它决定「下次读请求发给谁」。
- 默认值是
primary:所有读写都走主节点 → 没有读写分离 - 想让读走从节点,改用
secondary或secondaryPreferred:mongodb://.../?replicaSet=rs0&readPreference=secondary -
secondary要求至少有一个可用从节点,否则抛ReadPreferenceNotSatisfiedException;secondaryPreferred更稳妥:从节点挂光了就自动切回主节点读 - 注意:写操作永远只能发给主节点,这个行为不可更改,也不该尝试绕过
避免手动创建多个 MongoTemplate 的陷阱
网上很多教程教你在配置类里硬编码两个 MongoTemplate,分别连主库和从库地址——这会导致严重问题:
- 绕过副本集心跳机制:某个从节点宕机,你的「从库模板」仍会持续重试,不感知集群状态变化
- 事务失败:跨
MongoTemplate的操作无法加入同一事务,@Transactional失效 - Oplog 同步延迟被掩盖:你认为读的是从库,但实际可能连的是另一个主节点(比如故障转移后新主),而你没设
readConcern,读到的就是未确认写入 - 正确做法是只保留一个
@Primary MongoTemplate,靠 URI +readPreference统一调控
需要额外控制读一致性的场景
当业务要求「写完立刻能读到」(比如用户注册后马上查个人页),仅靠 readPreference=secondary 不够,因为副本集同步是异步的,存在延迟。
- 加
readConcern=majority:确保读到的数据已被大多数节点确认,降低脏读概率(但会增加延迟) - 对强一致性读,直接用
readPreference=primary,别强行塞到从节点 - 不要依赖
slaveOk=true这种旧参数,Spring Boot 3.x + MongoDB 6.x 已弃用 - 验证是否生效:开启 MongoDB 日志(
db.setLogLevel(1, "replication")),或在应用日志中搜ReadPreference和实际执行的 host:port
副本集读写分离的复杂点不在配置多难,而在于对 readPreference 行为的理解偏差——它不保证「永远不读主」,也不解决「写后立即读」,更不能替代合理的最终一致性设计。配错一个参数,可能让整个读流量悄悄打回主库,而你还在监控里看「从节点 CPU 很低」。











