尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
一个被 Spring Boot 静默吞掉的 Redis 连接池:主从读写分流的隐形 Bug
项目里本想做 Redis 主从读写分离——主库读写走主、只读场景走从。代码看着没毛病跑起来也不报错但实际上整个应用只剩一个连接池而且所有主库读全跑到了从库上。这篇记录一下这个静默 Bug 的定位过程与修复方案。一、背景项目里有两个 Redis 工具类分工明确RedisUtils主库读写封装各类数据结构的读写、分布式锁、菜单缓存等。ReplicaRedisUtils从读Replica Read只封装可容忍主从复制延迟的读方法对应主库方法的从库版。设计意图很清晰主库工厂(读主写) ──→ RedisUtils 主库读写 副本工厂(读从) ──→ ReplicaRedisUtils 只读从库两个LettuceConnectionFactory各带一个连接池互不干扰。二、代码看起来没问题主库配置RedisConfig.java6 个主库模板都按类型注入RedisConnectionFactory期望用 Spring Boot 自动装配的主库工厂BeanpublicRedisTemplateString,ObjectredisTemplate(RedisConnectionFactoryconnectionFactory){RedisTemplateString,ObjecttemplatenewRedisTemplate();template.setConnectionFactory(connectionFactory);// ... 序列化配置returntemplate;}副本配置ReplicaRedisConfig.java手动建了一个副本工厂ReadFrom SLAVE_PREFERRED4 个副本模板都挂它BeanpublicLettuceConnectionFactoryreplicaConnectionFactory(RedisPropertiesredisProperties){// ... 解析集群节点、密码、连接池参数LettucePoolingClientConfigurationbuilderLettucePoolingClientConfiguration.builder().readFrom(ReadFrom.SLAVE_PREFERRED).poolConfig(poolConfig);// ...returnnewLettuceConnectionFactory(clusterConfig,builder.build());}配置文件里连接池参数也齐全spring:redis:timeout:30000lettuce:pool:max-active:500max-idle:128max-wait:30000min-idle:64cluster:max-redirects:3nodes:xxxx编译通过、启动正常、读写都能跑——谁能想到这里其实已经坏了。三、实际发生了什么只剩一个池而且读全跑去了从库Spring Boot 自动装配主库连接工厂LettuceConnectionConfiguration.redisConnectionFactory上挂着一个条件BeanConditionalOnMissingBean(RedisConnectionFactory.class)LettuceConnectionFactoryredisConnectionFactory(...){...}注意它是按类型判断ConditionalOnMissingBean(RedisConnectionFactory.class)。而ReplicaRedisConfig里手动Bean创建的replicaConnectionFactory返回类型是LettuceConnectionFactory——它本身就是RedisConnectionFactory。用户自定义的Configuration先于自动配置注册于是启动时Spring 扫到replicaConnectionFactory注册为RedisConnectionFactory。轮到自动配置时ConditionalOnMissingBean(RedisConnectionFactory.class)判定为已存在→主库工厂被回退根本不创建。容器里最终只有一个RedisConnectionFactory就是副本工厂。那为什么不报错RedisConfig里每个模板方法签名都是(RedisConnectionFactory connectionFactory)没有Qualifier。容器里恰好只剩副本工厂这一个候选Spring 顺利把它注入进去——编译运行都不报错。所以最终的真实拓扑变成了副本工厂(读从, SLAVE_PREFERRED) ──→ RedisUtils 的 6 个模板 ← 意外本该用主库 ──→ ReplicaRedisUtils 的 4 个模板只有一个连接池副本那个max-active500主库工厂从未出生。四、影响场景预期实际RedisUtils写set/save.../tryLock/unlock走主库✅ 仍走主库集群模式下写命令始终由 slot owner/主节点处理不受ReadFrom影响RedisUtils读get/getStr/hgetall/hgetallMenu/loadCompressedMenu...走主库❌ 走从库SLAVE_PREFERRED有主从复制延迟读风险ReplicaRedisUtils读走从库✅ 走从库碰巧正确连接池数量主副 两个池❌ 只有一个池主副本全部流量共用最隐蔽的是写没出问题集群模式写的语义保护了它副本读碰巧正确只有本该读主库却读了从库这一类静默错位而且不报错、不影响启动纯靠业务层发现延迟问题才能察觉。五、根因小结一句话用一个LettuceConnectionFactory类型的 bean 想做额外的副本工厂结果它触发了自动配置的ConditionalOnMissingBean回退把主库工厂挤没了。Spring Boot 的ConditionalOnMissingBean按类型判断时子类/实现类的存在都会让条件成立。这是自定义 bean 接管自动配置的常见副作用——本意是扩展结果成了替换。六、修复方案核心显式声明主库工厂并标Primary让RedisConfig的模板按类型注入时选中主库副本模板继续用Qualifier指定副本工厂。ConfigurationpublicclassReplicaRedisConfig{BeanPrimarypublicLettuceConnectionFactoryprimaryLettuceConnectionFactory(RedisPropertiesredisProperties){returncreateLettuceConnectionFactory(redisProperties,null);// 不设 readFrom → 默认拓扑走主}BeanpublicLettuceConnectionFactoryreplicaConnectionFactory(RedisPropertiesredisProperties){returncreateLettuceConnectionFactory(redisProperties,ReadFrom.SLAVE_PREFERRED);}privateLettuceConnectionFactorycreateLettuceConnectionFactory(RedisPropertiesredisProperties,ReadFromreadFrom){RedisClusterConfigurationclusterConfigbuildClusterConfig(redisProperties);GenericObjectPoolConfig?poolConfigbuildPoolConfig(redisProperties);LettucePoolingClientConfiguration.LettucePoolingClientConfigurationBuilderbuilderLettucePoolingClientConfiguration.builder().poolConfig(poolConfig);if(readFrom!null){builder.readFrom(readFrom);}DurationtimeoutredisProperties.getTimeout();if(timeout!null){builder.commandTimeout(timeout);}LettuceConnectionFactoryfactorynewLettuceConnectionFactory(clusterConfig,builder.build());factory.afterPropertiesSet();returnfactory;}// ... buildClusterConfig / buildPoolConfig 省略两个工厂共用集群拓扑、密码、连接池参数BeanpublicRedisTemplateString,ObjectcustomStringRedisTemplateReplica(Qualifier(replicaConnectionFactory)LettuceConnectionFactoryreplicaConnectionFactory){// 注意 Qualifier 不能少否则被 Primary 抢走RedisTemplateString,ObjecttemplatenewRedisTemplate();template.setConnectionFactory(replicaConnectionFactory);// ...returntemplate;}}几个关键点主库工厂标PrimaryRedisConfig里 6 个模板按类型注入RedisConnectionFactory会优先选中主库工厂。副本模板参数必须加Qualifier(replicaConnectionFactory)这是另一个坑——标了Primary后按类型注入会优先选 primary若不显式Qualifier副本模板会被主工厂抢走等于白改。两工厂共用配置构建逻辑集群拓扑、密码、max-redirects、连接池、超时完全一致仅ReadFrom不同抽公共方法避免重复。RedisUtils和ReplicaRedisUtils不用改。修复后拓扑主库工厂(默认拓扑) ──→ RedisUtils 主库读写走主 副本工厂(SLAVE_PREFERRED) ──→ ReplicaRedisUtils 从读优先从库两个独立LettuceConnectionFactory各一个连接池主从分流真正生效。七、验证方法编译mvn compile通过。Bean 检查启动后访问/actuator/beans确认存在两个 beanprimaryLettuceConnectionFactoryprimaryreplicaConnectionFactory且不再有自动装配的redisConnectionFactory。运行时核对在RedisUtils的PostConstruct打印redisTemplate.getConnectionFactory()确认是primaryLettuceConnectionFactory与副本工厂不是同一个实例。八、经验教训ConditionalOnMissingBean按类型判断时要小心自定义 bean 的类型误伤自动配置。想加一个额外的同类型 bean往往会把自动配置的那个挤掉。需要自定义时最好显式声明并标Primary不要寄希望于自动配置的那个还在。“能跑不等于对”。这个 Bug 不报错、不影响启动、写正常、副本读碰巧正常只有主库读跑到从库这一条静默错位。连接池/读写路由类的 Bug最好用 Actuator bean 列表 运行时打印 connectionFactory 来核对。读写分离一定要验证ReadFrom实际生效在谁身上。写命令在集群模式下有 slot owner 保护读命令则完全受ReadFrom控制——读错库比写错库更难发现。Primary和Qualifier要配对用。标了Primary之后所有不该用 primary 的注入点都必须显式Qualifier否则会被静默抢走。附录环境Spring Boot 2.7.xspring-boot-maven-plugin 2.7.18Lettuce 6.2.4.RELEASERedis Cluster 模式commons-pool2 2.11.1
RELATED

相关推荐

钉钉告警发送钉钉群

钉钉告警发送钉钉群

主题:钉钉设置的告警所以需要在主题填写告警监控提示报警 {HOST.NAME} {ITEM.NAME} {ITEM.LASTVALUE} {EVENT.DATE} {EVENT.TIME}

📅 2026/11/9 18:47:27
2026答辩翻车真相!论文满分也过不了|90%人栽在临场细节问答盲区,一键补救

2026答辩翻车真相!论文满分也过不了|90%人栽在临场细节问答盲区,一键补救

答辩辅助平台https://www.okbiye.com 一、前言:2026答辩最冤真相,毁你的从来不是论文 很多毕业生直到答辩翻车都想不通:自己论文反复修改、查重全绿、导师定稿通过、盲审无问题,为什么现场答辩依旧拉胯、分数偏低? …

📅 2026/10/6 13:49:55
抖音质量效能部不传之秘:用AI精准预估“可能出事”的模块

抖音质量效能部不传之秘:用AI精准预估“可能出事”的模块

在抖音质量效能部搬砖那几年,每隔一段日子就会被拉进同一个“紧急群”——某个看起来人畜无害的代码提交,上线后把核心链路炸出一片红。复盘的时候大家总在叹气:要是能提前知道这模块“不干净”,哪怕多分一个测试人力进去也好啊。…

📅 2026/10/3 7:47:20
MORE NEWS

更多资讯

📰

工地太忙根本没时间复习考试 内部消息高压作业是指多少KV以上的电工作业

工地太忙根本没时间复习考试 内部消息高压作业是指多少KV以上的电工作业 你说工地天天赶工期,哪还有时间复习考试?这不,我昨天还接到一个商丘的兄弟电话,说他刚拿到高压电工证,正愁着怎么应付考试。他问的头一句话就是:“高压作业是指多少KV以上的…

📰

上饶县登高架设证报名千万别踩坑,这些坑你绝对不想踩

上饶县登高架设证报名千万别踩坑,这些坑你绝对不想踩 怕考不过白交培训费?上饶县登高架设证报名千万别踩坑,这些经验你得提前知道。很多人花了几千块报名培训,结果考试没通过,证书没拿到,钱白花了不说,还耽误了工作进度,这种事太常见了。…

📰

中级电工证报考的费用怎么算?官方报名入口在哪里找

中级电工证报考的费用怎么算?官方报名入口在哪里找 你是不是也在网上看到各种关于中级电工证的广告,不知道该信哪家,怕被中介坑?其实,现在考取电工证,关键就在于找到 官方报名入口…

📰

滑县电工证怎么办理 有真实经历的人才知道多关键

滑县电工证怎么办理 有真实经历的人才知道多关键 你是不是正为找工作发愁?手里缺个电工证,连面试资格都没?我认识一个三门峡水利工程的哥们儿,去年刚从工地回来,说就是因为没及时办理低压电工证,错失了几个好机会。他现在后悔得不行,说这证不光是上岗…

📰

电工证就是操作证吗官方回应新乡电工补证案例

电工证就是操作证吗官方回应新乡电工补证案例 证书过期了不知道怎么复审延期,这种事在新乡不少工地都发生过。前几天我接到一个老电工老李的电话,说他去年刚换的新电工证,结果在项目上检查时被说“证过期了”,让他当场懵了。老李说,他记得明明是2023…

📰

吴忠低压电工证多少钱?学历不高也能考,真能改变收入

吴忠低压电工证多少钱?学历不高也能考,真能改变收入 你是不是也担心自己学历不高,报不了低压电工证?其实不然,我认识不少初中毕业的电工,现在拿着低压电工证,收入比以前翻了几倍。今天咱们就来聊聊【吴忠低压电工证】,多少钱能考下来,怎么报,怎么考…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬