先说个自己的感受:很多人在Spring Boot里用Redis,最开始都是照着demo敲一个spring.redis.host=127.0.0.1就完事了。等哪天服务一重启、Redis主节点一挂,才发现自己根本不了解手上这套缓存到底是怎么部署的。单机、主从、哨兵、集群这四种模式,在Spring Boot里的配置方式差别不小,踩坑点也完全不同。这篇文章我把自己在实际项目中配置这四种模式的经验整理了一遍,从依赖、配置、参数含义到常见故障,一次性说清楚,适合正在搭Redis环境或者想从单机往高可用架构迁移的团队参考。
1. Redis四种模式的核心差异与选型逻辑
配置任何一种模式之前,得先搞清楚一件事:这四种模式到底为了解决什么问题。很多方案选错,不是因为技术不行,而是需求没分析清楚。我见过有团队业务量一天几千次请求,非得上三主三从集群,维护成本远超收益;也有团队核心支付链路只用了单机Redis,半夜主节点宕机直接业务停摆。选型的关键是数据量、高可用要求、读写并发和运维成本这四个维度的匹配。
1.1 单机模式:最小可用,适合开发环境与低并发场景
单机模式就是只有一个Redis实例,所有读写都打在这一个节点上。这种模式的配置最简单,application.yml里写几行就完事,不需要考虑主从同步、哨兵选举、集群分片这些复杂概念。但它有两个硬伤:一是数据不冗余,机器磁盘坏了数据就没了;二是进程挂了不会自动恢复,需要人工介入重启。
单机模式适合什么场景?本地开发环境、测试环境、并发量很低的内部管理系统,以及那些允许缓存丢失后重新加载的业务。我在微服务项目里通常把验证码、短链接这类短生命周期数据放单机Redis,即便服务重启丢一点也影响不大。如果你正在学习Redis,从单机模式开始是最合理的路径,先把连接、序列化、缓存注解这些基础玩明白。
1.2 主从复制:数据冗余与读写分离
主从复制模式由一个主节点和多个从节点组成,主节点负责写,数据通过异步复制同步到从节点,从节点可以分担读流量。这种模式相比单机解决了两件事:第一是数据冗余,主节点挂了从节点还有数据;第二是读写分离,把读压力分散到多个节点上。
但它有一个关键缺陷:没有自动故障转移。主节点宕机后,从节点不会自动升级为新的主节点,需要人工执行REPLICAOF NO ONE来手动提升,这个过程业务会有中断。主从复制是异步的,主节点突然宕机时,还没来得及复制到从节点的数据会丢失。这里的权衡需要业务方提前知晓。
1.3 哨兵模式:在高可用上补齐短板
哨兵模式在主从复制基础上引入了监控程序Sentinel,它会持续监控主从节点的健康状态。当主节点被判定为客观下线后,哨兵集群会通过选举从从节点中选出一个新的主节点,并把故障转移的结果通知给客户端。整个切换过程是自动的,通常几十秒内完成。
哨兵模式的关键在于Sentinel本身也要集群化部署,生产环境至少启动3个Sentinel实例。为什么是3个而不是1个?因为哨兵要确定主节点是否真的挂了,需要多数派投票,3个节点挂掉1个仍然能形成多数派,而2个节点挂掉1个就只剩50%无法投票。哨兵模式适合需要自动故障恢复、但数据量还在单机可承载范围的业务,大部分中大型应用的Redis高可用方案落在这里。
1.4 集群模式:横向扩展解决容量瓶颈
当单节点的内存已经无法满足数据量需求时,就需要集群模式了。Redis Cluster采用无中心化架构,数据被自动分片到16384个哈希槽中,每个节点负责一部分槽。比如三主三从的集群,三个主节点各自分担约5461个槽位,写入时客户端根据key的哈希值自动路由到对应节点。
集群模式同时解决了容量和可用性问题:横向加节点就能扩容,每个主节点挂一个从节点,主节点挂了从节点自动顶上。但它也带来了新的复杂度,最典型的是多key操作受限,如果两个key经过哈希计算落在不同节点上,执行MGET、事务等操作会直接报错。集群模式适合数据量大、写入并发高、需要水平扩展的业务,比如用户量千万级以上的平台缓存层。
1.5 选型决策表
| 模式 | 数据容量 | 自动故障转移 | 读写扩展性 | 运维复杂度 | 推荐场景 |
|---|---|---|---|---|---|
| 单机 | 单节点上限 | 不支持 | 不支持 | 极低 | 开发测试环境、低并发内部系统 |
| 主从 | 单节点上限 | 不支持,需人工提升 | 读可扩展 | 低 | 有读多写少、可容忍短暂中断的业务 |
| 哨兵 | 单节点上限 | 支持,自动切换 | 读可扩展 | 中 | 生产核心业务,数据量未超单机上限 |
| 集群 | 水平扩展 | 支持,自动切换 | 读写均可扩展 | 高 | 数据量大、写入并发高的规模化业务 |
2. 基础工程搭建:依赖、连接池与验证
无论最终用哪种模式,Spring Boot工程的依赖和基础配置都是前置条件。这里的坑不少,尤其版本差异经常导致配置不生效,我先说清楚基础部分。
2.1 引入依赖时的版本差异问题
Spring Boot 2.x和3.x对Redis的配置前缀不一样。2.x用的是spring.redis.*,从Spring Boot 3.0开始改成了spring.data.redis.*。很多团队升级到3.x后一堆连接报错,原因就是配置文件里的前缀还留着旧的。如果你用的是2.x,保持spring.redis.host这种写法;如果是3.x,务必换成spring.data.redis.host。下面以Spring Boot 2.x为例给出引入依赖的pom片段,3.x的依赖坐标不变,只是配置前缀需要调整:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency>spring-boot-starter-data-redis默认引入的是Lettuce客户端,这是Spring官方推荐的连接器。第二个依赖commons-pool2很多人会忽略,但它决定了lettuce.pool.*下的连接池参数是否生效。Spring Boot的自动配置只有在classpath里检测到commons-pool2时才会创建连接池,没有这个依赖,池化配置会被静默忽略。
2.2 基础配置模板与参数含义
一个标准的单机Redis配置如下:
spring: redis: host: 127.0.0.1 port: 6379 password: 123456 database: 0 timeout: 3s lettuce: pool: max-active: 8 max-idle: 8 min-idle: 0 max-wait: -1ms逐个说下这些参数的作用:host和port不用多讲,是Redis服务端地址;password对应Redis的requirepass配置,如果Redis端没设密码这里留空即可,注意设置了密码但客户端不写,启动时不一定报错,而是第一次访问时报NOAUTH Authentication required;database是逻辑库编号,Redis默认有16个库(0到15),默认连接0号库;timeout是连接超时时间,建议别设太长,3秒比较合理,否则Redis假死时客户端会长时间卡住。
连接池四个参数在并发高的时候很关键。max-active是连接池最大连接数,max-idle是最大空闲连接数,min-idle是保持的最小空闲连接数,max-wait是获取连接的最大等待时间。-1ms表示无限等待,这个值我建议生产环境不要用,一旦连接池耗尽,所有请求线程都会阻塞在获取连接上,很容易引发雪崩,通常设置5000ms左右比较稳妥。
2.3 验证连通性:最小可用代码
配置完成之后,我习惯写一个最简单的测试来验证连接是否正常,而不是直接跑到业务代码里去调试:
@SpringBootTest class RedisConnectionTest { @Autowired private StringRedisTemplate stringRedisTemplate; @Test void testConnection() { stringRedisTemplate.opsForValue().set("test:connection", "ok"); String value = stringRedisTemplate.opsForValue().get("test:connection"); Assertions.assertEquals("ok", value); } }这里有一个细节:用StringRedisTemplate而不是直接注入RedisTemplate。RedisTemplate默认使用JdkSerializationRedisSerializer,写入Redis的数据会带一堆二进制头,用可视化工具查看时是乱码。StringRedisTemplate直接存储字符串,阅读和排查都方便很多。如果需要存取对象,建议单独配置序列化方式,这一点在后面实操部分详细展开。
3. 单机模式配置实操与RedisTemplate序列化
单机模式是最常用的起步方案,配置虽然简单,但序列化器和连接池这两个细节决定了后续使用的体验。
3.1 单机模式的完整配置清单
单机模式下,spring.redis.*的核心配置已经在上文给出。除了那些基础参数,有几个容易被忽略的配置项也值得关注。ssl配置,如果你的Redis服务端启用了SSL加密,需要设置spring.redis.ssl=true,这种场景多见于云厂商提供的数据服务;client-name参数可以在Redis的CLIENT LIST中标识出客户端来源,排查问题时有帮助;lettuce.shutdown-timeout控制关闭连接池时的等待时间,默认100ms,如果你的操作比较重,可以调到200ms以上。
在Spring Boot 2.x中还有一个需要注意的地方是LettuceConnectionFactory默认创建的连接是否共享。默认情况下shareNativeConnection=true,所有操作共用一个底层连接,这在多数场景下没有问题,因为Lettuce支持多路复用。但如果你的代码里有长事务、LUA脚本、或者某些需要独占连接的操作,需要设置shareNativeConnection=false,否则可能出现连接被占用的异常。
3.2 为什么连接池在Lettuce下依然重要
Lettuce本身是基于Netty的异步客户端,单条连接可以并发处理大量请求,不像Jedis那样每次操作都要从池里借连接。那为什么还需要连接池?核心是为了兜底。没有连接池限制时,如果某个时刻出现了流量尖峰,Lettuce会往Redis服务端打大量并发命令,而Redis是单线程处理命令的,超出处理能力的请求全部排队,放大延迟。
连接池更像一个流量阀门。max-active=8意味着同时最多只有8个连接在工作,每个连接上的命令可以并发,但连接数总量可控。实际调优时我一般看两个指标:一是Redis服务端的connected_clients,二是应用端的活跃连接数。如果连接数长期接近上限,说明max-active设小了;如果大部分时间连接很空闲,则说明资源有浪费。单机模式下的初始配置可以从max-active=16, max-idle=16, min-idle=4起步,再根据压测结果调整。
3.3 RedisTemplate配置:对象序列化问题
直接用默认的RedisTemplate存对象时,你会发现Redis里的value是一串类似\xAC\xED\x00\x05t...的二进制内容,这就是JDK序列化的结果,它让数据变得不可读、体积膨胀严重,而且Redis Desktop Manager这类工具里看到的是乱码。因此实际项目中我一般会单独配置一个RedisTemplate的Bean:
@Configuration public class RedisConfig { @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(factory); StringRedisSerializer stringSerializer = new StringRedisSerializer(); GenericJackson2JsonRedisSerializer jsonSerializer = new GenericJackson2JsonRedisSerializer(); template.setKeySerializer(stringSerializer); template.setHashKeySerializer(stringSerializer); template.setValueSerializer(jsonSerializer); template.setHashValueSerializer(jsonSerializer); template.afterPropertiesSet(); return template; } }这里key统一用String序列化,value用JSON序列化。GenericJackson2JsonRedisSerializer会在序列化时保留类型信息,反序列化时能还原成正确的Java对象。配置完成后再配合@EnableCaching和@Cacheable注解使用,缓存对象就会以JSON格式存储,可读性高,也便于跨语言消费。有一点要注意,带泛型的复杂对象用Jackson序列化时可能丢失泛型信息,这种情况建议改用自定义的ObjectMapper或干脆将对象转成JSON字符串再存。
4. 主从模式配置:读写分离的正确落地方式
主从模式表面上就是多配一个从节点,但在Spring Boot里让应用真正“读从节点”并不是默认行为,这是很多人配置完以为成功、实际流量全在主节点上的原因。
4.1 用Docker快速搭建一主一从环境
本地验证主从模式最快捷的方式是使用Docker Compose。下面这个配置启动两个Redis实例,redis-master监听6379端口,redis-replica监听6380端口并自动成为master的从节点:
version: '3.8' services: redis-master: image: redis:7.0 container_name: redis-master ports: - "6379:6379" command: ["redis-server", "--requirepass", "123456", "--appendonly", "yes"] redis-replica: image: redis:7.0 container_name: redis-replica ports: - "6380:6379" depends_on: - redis-master command: ["redis-server", "--slaveof", "redis-master", "6379", "--masterauth", "123456", "--requirepass", "123456"]两个关键点:第一,redis-replica使用--slaveof指定主节点地址,容器网络内通过服务名redis-master解析;第二,主从都设了密码时,从节点必须通过--masterauth提供主节点的认证密码,否则复制链路会反复断连。启动完成后,在容器里执行docker exec -it redis-master redis-cli -a 123456 info replication能查看到role:master和连接中的从节点。
4.2 主从模式下Spring Boot的配置真相
这里有必须提前说明的坑:Spring Boot自带的spring.redis.*配置只支持连接一个Redis地址。在主从架构里,这个地址是主节点还是从节点完全由你决定,框架层面没有提供“自动识别主从”的能力。很多文章说主从模式下Spring Boot会自动读写分离,那是误解。默认配置下,所有读写都走你填写的那个地址。
要在Spring Boot里真正实现读写分离,常见做法是手动定义两个LettuceConnectionFactory,一个指向主节点用于写,一个指向从节点用于读,然后在业务层通过AOP或路由注解动态切换。但这个方案的复杂度不低,而且需要考虑事务内必须走主库、强一致读必须走主库等细节。如果业务对数据实时性要求不高,还有一个简化方案:使用Lettuce的readFrom配置:
@Bean public LettuceClientConfigurationBuilderCustomizer lettuceClientConfigurationCustomizer() { return builder -> builder.readFrom(ReadFrom.REPLICA_PREFERRED); }ReadFrom.REPLICA_PREFERRED表示优先从从节点读取,从节点不可用时才回退到主节点。ReadFrom.REPLICA则强制只读从节点,从节点挂了直接报错。这里要理解,readFrom只影响当前连接工厂指向的那个Redis拓扑中主从角色的选择,前提是连接工厂必须配置为主节点地址,否则客户端根本感知不到从节点存在。
4.3 主从延迟与数据一致性注意事项
主从复制是异步的,从节点数据总是领先主节点一段时间的。在正常情况下延迟在毫秒级,但主节点压力大或者网络抖动时延迟可能飙到秒级。我踩过的一个真实案例是:用户在页面上提交订单后立即查询列表,列表接口走了从库,查不到刚写入的数据,用户以为下单失败反复提交,产生了重复订单。后来排查发现主从延迟一度达到3秒。
对于这类强一致读场景,规避手段有三种:一是把实时性要求高的读操作强制路由到主库;二是对于刚写入的数据写入一个本地标记,一段时间内这些key的读走主库;三是直接放弃从库读,从库只承担离线分析和数据备份的职能。主从延迟的实时监控也不难,通过INFO replication命令查看从节点的master_repl_offset和主节点的差距,差距持续变大说明复制链路有问题,常见原因是从节点带宽不足或主节点写入了大key。
5. 哨兵模式配置:从手动故障恢复走向自动选举
主从模式最大的痛点是主节点挂掉后需要人工介入,哨兵模式就是来解决这个问题的。但哨兵的配置坑同样不少,尤其是哨兵本身和Spring Boot客户端的配合逻辑,需要理解到位。
5.1 哨兵系统结构与关键参数
一个最小的高可用哨兵架构包含1个主节点、2个从节点、3个哨兵实例。哨兵节点并非Redis数据节点,它不存储业务数据,只负责监控、通知和自动故障转移。哨兵的核心配置如下:
sentinel monitor mymaster 127.0.0.1 6379 2 sentinel down-after-milliseconds 5000 sentinel failover-timeout 60000 sentinel parallel-syncs 1 sentinel auth-pass mymaster 123456逐行解释:sentinel monitor mymaster 127.0.0.1 6379 2定义被监控的主节点,mymaster是这个主节点的逻辑名称,2是quorum值,表示至少有2个哨兵同意主节点不可达时才触发故障转移。down-after-milliseconds 5000是主节点无响应多少毫秒后判定主观下线。failover-timeout 60000是故障转移超时时间。parallel-syncs 1表示新主节点选定后,同时允许几个从节点同步数据,设1可以减轻主节点压力。sentinel auth-pass mymaster 123456必须和Redis主从节点的requirepass一致,否则哨兵无法正常通信,会一直误判主节点宕机。
5.2 Spring Boot连接哨兵的配置方式
在Spring Boot里配置哨兵模式,不用再写具体的主节点地址,而是告诉客户端哨兵在哪里、要跟踪哪个主节点。客户端启动时会通过哨兵获取当前的主节点地址,哨兵切换完成后也会收到通知更新连接:
spring: redis: sentinel: master: mymaster nodes: 127.0.0.1:26379,127.0.0.1:26380,127.0.0.1:26381 password: 123456这里的spring.redis.password是Redis数据节点的认证密码,哨兵节点本身默认不设密码,如果哨兵节点也用requirepass设置了密码,需要在sentinel.conf的sentinel auth-pass中额外配置。重点确认以上配置里master名称mymaster和哨兵监控的master名称保持一致,否则客户端会一直报错找不到主节点。
用下面的命令可以验证哨兵配置是否正确:
redis-cli -p 26379 sentinel get-master-addr-by-name mymaster能返回主节点的IP和端口,说明哨兵认为当前主节点正常。再执行redis-cli -p 26379 sentinel replicas mymaster查看从节点列表是否完整。
5.3 故障转移时客户端的表现与踩坑记录
哨兵模式下最常见的线上问题不是哨兵切换失败,而是客户端在切换后没有正确重连到新主节点。Lettuce客户端本身支持哨兵模式下的自动拓扑感知,但在Spring Boot 2.2之前的版本,Lettuce存在连接池持有旧连接不复用的问题,导致故障切换后应用持续报错。如果遇到这种情况,建议先把Spring Boot升级到2.3或以上版本,同时可以配置连接工厂的validateConnection=true,在借出连接时主动校验连接是否有效:
spring: redis: lettuce: pool: max-active: 16 # 高版本Lettuce默认会注册哨兵拓扑刷新,无需额外配置另外要注意哨兵模式下的分布式锁问题。如果使用了Redis实现分布式锁,主节点挂掉的瞬间,锁数据还没同步到从节点,新主节点上锁不存在,可能出现两个客户端同时持有锁的情况。严格意义上这属于哨兵模式的高可用缺陷,如果业务对分布式锁的可靠性有极高要求,建议使用Redisson并开启RedLock模式,或者直接采用ZooKeeper / etcd这类强一致方案。
6. 集群模式配置:数据分片与客户端路由
集群模式是四种模式里配置最复杂的,它的核心不是“连接多个节点”,而是理解数据如何在节点间分布。Spring Boot配置集群模式相对简洁,但用起来有非常多隐性约束。
6.1 三主三从集群的搭建与验证
生产环境的Redis Cluster建议6个节点起步,3个主节点各带1个从节点。使用redis-cli的集群管理命令可以快速搭建:
redis-cli --cluster create \ 192.168.1.10:7001 192.168.1.11:7002 192.168.1.12:7003 \ 192.168.1.13:7004 192.168.1.14:7005 192.168.1.15:7006 \ --cluster-replicas 1 -a 123456其中--cluster-replicas 1表示每个主节点配1个从节点。执行后redis-cli会输出一份哈希槽分配方案,确认无误后输入yes完成创建。搭建完成后,执行redis-cli -c -p 7001 -a 123456 cluster info查看cluster_state:ok,再执行redis-cli -c -p 7001 -a 123456 cluster nodes查看每个节点的角色和槽位范围。
6.2 Spring Boot集群模式配置与参数解析
Spring Boot连接集群的配置也比较直白:
spring: redis: cluster: nodes: - 192.168.1.10:7001 - 192.168.1.11:7002 - 192.168.1.12:7003 - 192.168.1.13:7004 - 192.168.1.14:7005 - 192.168.1.15:7006 max-redirects: 3 password: 123456nodes列表只需要提供部分节点给客户端做引导,客户端拿到节点信息后会自动获取整个集群的拓扑,包括所有节点的地址和槽位分布。max-redirects设置的是客户端遇到MOVED重定向时最多跟随跳转的次数。执行一个命令时,客户端根据key的哈希结果判断目标槽位,如果请求发到了一个不负责该槽位的节点,该节点会返回MOVED指令和正确节点的地址,客户端重发命令到正确节点。正常情况下最多跳转一次,但在集群拓扑变更、客户端缓存未更新的场景下可能出现多次重定向,max-redirects是兜底保护,建议设3。
6.3 多key操作限制与hash tag解决方案
集群模式最大的使用限制在多key操作上。比如MGET key1 key2要求所有key的哈希槽一致,如果两个key落在不同节点,Redis会返回CROSSSLOT Keys in request don't hash to the same slot错误。这个问题同样影响DEL、RENAME、SINTER这类命令,以及Redis事务和Lua脚本中涉及多个key的操作。
解决办法是使用hash tag。Redis计算key的哈希槽时,如果key中包含花括号,例如{user123}:profile,只对花括号内的user123计算哈希值。这样同一个用户的多个key都会落在同一个槽位上,可以使用MGET批量获取。具体业务建模时,我习惯于把关联性强的key设计成带有相同hash tag的格式,比如用户维度数据统一用{userId}:orders、{userId}:cart。hash tag不要滥用,因为设计不当会把大量key集中到少数槽位,造成数据倾斜,热点集中在某几个节点上。
6.4 集群拓扑刷新与Lettuce的适配细节
Lettuce客户端在集群模式下有两个拓扑刷新机制值得了解。一种是自适应刷新,当客户端收到MOVED重定向时,会主动触发拓扑更新;另一种是周期性刷新,通过以下配置开启:
spring: redis: lettuce: cluster: refresh: adaptive: true period: 10s自适应刷新适合集群节点变化比较频繁的场景,比如频繁扩缩容;周期性刷新适合节点稳定、但担心客户端拓扑信息滞后的场景。两个同时开启也算常见做法,代价是多了一点网络开销。实际中如果某个key执行时报CLUSTERDOWN The cluster is down,大概率是集群本身处于故障状态,要先看cluster_state,而不是怀疑客户端配置。
集群模式下执行KEYS和SCAN命令也要小心。KEYS命令在集群模式下会广播到所有节点,数据量大时直接卡死Redis;SCAN虽然可以用COUNT控制每次返回量,但在集群模式下需要在每个节点上分别执行一次。我建议业务代码里避免使用这两个命令,如果需要遍历数据,可以使用专门的离线工具在Redis的备份实例上操作。
7. 常见问题排查与优化实战实录
配置部分讲完了,最后把我在实际项目中遇到的典型问题和定位方法整理成一份速查表,什么时候该怀疑Redis配置,什么时候该怀疑客户端版本,照着查能省下不少排查时间。
7.1 问题定位速查表
| 现象 | 优先级排查方向 | 常见原因与处理方式 |
|---|---|---|
服务启动时报Unable to connect to Redis | 网络与绑定的连通性 | 是否跨网段访问;Redis是否只绑定了127.0.0.1;检查protected-mode |
第一次访问时报NOAUTH Authentication required | 密码配置 | requirepass和spring.redis.password是否一致;主从/哨兵模式下masterauth是否配置 |
| 3.x项目配置不生效 | 配置前缀 | Spring Boot 3.x改用spring.data.redis.*,同时JDK版本需17+ |
哨兵模式报ERR unknown sentinel或找不到master | 逻辑名称配置 | spring.redis.sentinel.master必须与哨兵配置文件里的master名称完全一致 |
集群模式执行批量命令报CROSSSLOT错误 | key设计 | 使用{commonTag}hash tag让关联key落到同一槽位 |
| 故障转移后长时间无法恢复 | Lettuce版本 | 升级Spring Boot到2.3+;检查连接池validateConnection设置 |
| 连接池耗尽导致线程阻塞 | 池参数 | 调大max-active;检查是否有慢命令占用了连接;max-wait不要设置为无限 |
7.2 连接池与并发参数的调优心得
Redis的连接池参数没有一劳永逸的标准答案,但可以通过压测快速找到合适区间。我的经验是:如果应用QPS在几千级,max-active设16到32足够,min-idle设4到8可以避免流量突增时的冷启动;如果QPS过万,优先考虑的是Redis服务端能扛多少QPS而不是盲目调大连接池,因为Redis是单线程模型,CPU达到上限时增加连接数只会加剧延迟。
关于max-wait,默认的-1ms表示无限等待,这个值必须改掉。原因很好理解:一旦连接池被打满,所有需要连接的线程都会卡在等待队列里,线程数持续堆积,随之而来的是内存溢出和超时雪崩。生产环境我通常设置max-wait: 3000ms,等不到连接直接抛异常,至少让上游服务能快速失败。
还有一个容易被忽视的配置是timeout,它控制的是建立连接的超时时间,偏向网络层。有些业务会把Redis超时时间调得很长来规避偶发网络抖动,但副作用是Redis真不可用时接口响应也变得很慢。我一般设置3秒,配合连接池的max-wait一起控制整体响应时间。
7.3 结合Actuator健康检查与监控指标
Spring Boot工程引入spring-boot-starter-actuator后,Redis的健康状态会自动接入/actuator/health。配置management.endpoint.health.show-details=always后,可以查看到Redis是否连接正常,连接工厂类型是Lettuce还是Jedis。在多实例部署中,如果某个实例的Redis连接异常,通过健康检查能第一时间把流量摘除。
要监控更细粒度的指标,可以引入micrometer-registry-prometheus,Spring Boot会自动采集Lettuce连接池的状态指标,比如lettuce_commands_attempted、lettuce_commands_completed等。我在生产环境里关注最多的三个指标是:连接池活跃连接数、命令执行成功率和平均耗时。平均耗时突然飙升时,通常不是Redis本身的问题,而是应用里的热门key集中到了同一个Redis节点上,造成单热点。
7.4 与可视化工具的配合使用
排查问题时我常用Another Redis Desktop Manager和RedisInsight这类可视化客户端。连接单机/主从模式时直接用host和port连接主节点即可;连接哨兵模式时,工具里有专门的Sentinel连接类型,填写哨兵地址和master名称即可自动跟随当前主节点;连接集群模式时,填写任意一个节点地址,工具会自动识别整个集群拓扑。
有一个实用技巧:当生产环境启用了密码连接时,先在可视化工具里把连接配置调通,再回过去对照Spring Boot里的配置,很多密码遗漏、IP写错、端口混淆的问题都能提前暴露。我遇到过好几次类似情况——Spring Boot配置写得看起来没问题,但实际在工具里连都连不上,说明问题根本不在应用侧,而在Redis服务端的绑定和防火墙配置上。
7.5 一个值得养成的排查习惯
无论哪种模式,我建议排查链路先从Redis服务端确认状态,再到Spring Boot客户端确认配置,最后才看业务代码。具体来说,先在命令行用redis-cli -a 密码 ping确认能返回PONG;再执行redis-cli -a 密码 info replication或cluster info确认当前模式下的节点角色和集群状态;最后才去看应用的配置文件。这个顺序能避免做很多无用功,因为大部分连接问题根源在Redis端,比如bind 127.0.0.1导致其他机器访问不了、protected-mode yes限制了远程连接、密码不一致导致复制中断等。
在我自己维护的项目里,配置Redis从来不是写完配置文件就结束的事。单机模式是基本功,主从模式是入门高可用的第一课,哨兵模式让系统具备自动恢复能力,集群模式则打开了水平扩展的天花板。每一种模式背后对应的是业务对可靠性、容量、复杂度之间不同的权衡。从单机迁移到哨兵时,先确保主从复制和哨兵本身的故障转移流程都验证通过;从哨兵迁移到集群时,重点关注代码里的多key操作和业务数据分布。把底层的节点角色、数据分片、故障转移机制都搞清楚,配置文件里的每行参数才真正掌握在你手里,而不是依赖网上的模版。