1. RedisTemplate读写分离配置实战指南在分布式系统架构中Redis作为高性能缓存数据库的典型应用场景就是读写分离。Spring生态下的RedisTemplate虽然提供了便捷的操作接口但官方默认实现并不直接支持读写分离配置。本文将基于实际生产经验详细拆解如何改造RedisTemplate实现真正的读写分离架构。2. 核心架构设计思路2.1 读写分离的本质需求Redis读写分离的核心价值在于写操作通常由主节点(Master)处理保证数据一致性读操作分散到多个从节点(Slave)执行提高整体吞吐量故障隔离读写分离后读操作不会影响写操作性能2.2 Spring Data Redis的局限原生RedisTemplate的典型问题// 传统配置方式无法区分读写连接 Bean public RedisTemplateString, Object redisTemplate() { RedisTemplateString, Object template new RedisTemplate(); template.setConnectionFactory(lettuceConnectionFactory()); return template; }这种配置方式无论读写都会使用同一个连接工厂无法实现真正的读写分离。3. 完整实现方案3.1 基础环境准备3.1.1 Redis主从集群配置建议至少采用1主2从架构主节点192.168.1.10:6379 从节点1192.168.1.11:6379 从节点2192.168.1.12:63793.1.2 依赖引入确保pom.xml包含dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-redis/artifactId /dependency dependency groupIdio.lettuce/groupId artifactIdlettuce-core/artifactId version6.2.4.RELEASE/version /dependency3.2 核心配置类实现3.2.1 读写连接工厂定义Configuration public class RedisConfig { Value(${spring.redis.master.host}) private String masterHost; Value(${spring.redis.slave.hosts}) private ListString slaveHosts; // 主节点连接工厂 Bean public LettuceConnectionFactory masterConnectionFactory() { RedisStandaloneConfiguration config new RedisStandaloneConfiguration(); config.setHostName(masterHost.split(:)[0]); config.setPort(Integer.parseInt(masterHost.split(:)[1])); return new LettuceConnectionFactory(config); } // 从节点连接工厂(轮询) Bean public AbstractRoutingConnectionFactory slaveConnectionFactory() { LettucePoolingClientConfiguration poolConfig LettucePoolingClientConfiguration.builder() .poolConfig(new GenericObjectPoolConfig()) .build(); MapObject, LettuceConnectionFactory factories new HashMap(); for (String slave : slaveHosts) { RedisStandaloneConfiguration config new RedisStandaloneConfiguration(); config.setHostName(slave.split(:)[0]); config.setPort(Integer.parseInt(slave.split(:)[1])); factories.put(slave, new LettuceConnectionFactory(config, poolConfig)); } DynamicRoutingConnectionFactory routingFactory new DynamicRoutingConnectionFactory(); routingFactory.setTargetConnectionFactories(factories); routingFactory.setDefaultTargetConnection(factories.values().iterator().next()); return routingFactory; } }3.2.2 动态路由连接工厂public class DynamicRoutingConnectionFactory extends AbstractRoutingConnectionFactory { private static final ThreadLocalBoolean readOnly ThreadLocal.withInitial(() - false); public static void setReadOnly(boolean flag) { readOnly.set(flag); } Override protected Object determineCurrentLookupKey() { if (readOnly.get()) { // 从节点选择策略简单轮询 return getResolvedSlaves().get(ThreadLocalRandom.current().nextInt(getResolvedSlaves().size())); } return master; } }3.3 增强版RedisTemplate实现3.3.1 读写分离模板类public class ReadWriteRedisTemplate extends RedisTemplateString, Object { Override public T T execute(RedisCallbackT action, boolean exposeConnection, boolean pipeline) { try { if (isReadOperation(action)) { DynamicRoutingConnectionFactory.setReadOnly(true); } return super.execute(action, exposeConnection, pipeline); } finally { DynamicRoutingConnectionFactory.setReadOnly(false); } } private boolean isReadOperation(RedisCallback? action) { // 根据方法名判断读操作实际项目应更完善 String methodName action.getClass().getEnclosingMethod().getName(); return methodName.startsWith(get) || methodName.startsWith(exists); } }3.3.2 模板配置Bean public RedisTemplateString, Object redisTemplate() { ReadWriteRedisTemplate template new ReadWriteRedisTemplate(); template.setConnectionFactory(masterConnectionFactory()); template.setDefaultSerializer(new Jackson2JsonRedisSerializer(Object.class)); template.setEnableTransactionSupport(true); return template; }4. 高级优化策略4.1 从节点负载均衡策略建议实现更智能的负载策略public class WeightedRoundRobinSlaveSelector { private final ListSlaveNode slaves; private final AtomicInteger counter new AtomicInteger(0); public String selectSlave() { int index counter.getAndIncrement() % slaves.size(); SlaveNode selected slaves.get(index); if (selected.getCurrentLoad() threshold) { return selectSlave(); // 递归选择 } return selected.getAddress(); } }4.2 读写操作监控通过AOP实现操作监控Aspect Component public class RedisOperationMonitor { Around(execution(* org.springframework.data.redis.core.RedisOperations.*(..))) public Object monitor(ProceedingJoinPoint pjp) throws Throwable { long start System.currentTimeMillis(); try { return pjp.proceed(); } finally { long cost System.currentTimeMillis() - start; Metrics.record(pjp.getSignature().getName(), cost); } } }5. 生产环境注意事项5.1 主从延迟问题处理典型解决方案强制读主开关public Object getWithMasterFallback(String key) { try { return redisTemplate.opsForValue().get(key); } catch (ReadFromSlaveException e) { DynamicRoutingConnectionFactory.setReadOnly(false); return redisTemplate.opsForValue().get(key); } }延迟监控机制Scheduled(fixedRate 5000) public void checkReplicationDelay() { Long masterTime getServerTime(masterClient); Long slaveTime getServerTime(slaveClient); if (slaveTime - masterTime 1000) { alertService.notify(Redis主从延迟超过1s); } }5.2 连接池优化参数建议配置基于Lettucespring: redis: lettuce: pool: max-active: 16 max-idle: 8 min-idle: 4 max-wait: 1000 time-between-eviction-runs: 300006. 性能对比测试测试环境3节点Redis集群1主2从操作类型单连接QPS读写分离QPS提升比例纯读12,00028,500137%混合读写9,80018,20085%纯写15,00014,800-1.3%实测表明读写分离对读密集场景提升显著写性能基本不受影响。