1. Redis读写分离架构解析Redis作为高性能键值数据库在实际生产环境中经常会遇到读多写少的场景。我经历过一个电商项目大促期间读请求量达到写请求的20倍以上单节点Redis实例的CPU直接飙到100%。这时候就需要引入读写分离架构来分担主节点压力。读写分离的核心思想很简单让主节点(Master)只处理写操作而将读请求分散到多个从节点(Slave)上执行。这种架构下从节点通过异步复制机制与主节点保持数据同步。根据Redis官方文档从节点每秒能处理10万的读请求这意味着理论上每增加一个从节点就能线性提升系统的读吞吐量。重要提示Redis的复制是异步的这意味着从节点的数据会有短暂延迟。对数据强一致性要求极高的场景如金融交易需要谨慎评估。2. 环境准备与基础配置2.1 Redis主从集群搭建首先需要准备至少两个Redis实例。在我的测试环境中使用了Redis 6.2版本主节点配置保持默认从节点需要修改redis.conf# 从节点配置 replicaof 主节点IP 6379 replica-read-only yes验证主从复制是否正常工作# 在主节点执行 redis-cli set test_key hello # 在从节点执行 redis-cli get test_key2.2 Spring Boot项目初始化创建一个基础Spring Boot项目添加必要依赖dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-redis/artifactId /dependency dependency groupIdorg.apache.commons/groupId artifactIdcommons-pool2/artifactId /dependency3. RedisTemplate高级配置3.1 连接工厂配置核心在于配置LettuceConnectionFactorySpring Boot 2.x默认使用Lettuce客户端Configuration public class RedisConfig { Bean public LettuceConnectionFactory redisConnectionFactory() { RedisStandaloneConfiguration masterConfig new RedisStandaloneConfiguration(); masterConfig.setHostName(主节点IP); masterConfig.setPort(6379); RedisStandaloneConfiguration slaveConfig new RedisStandaloneConfiguration(); slaveConfig.setHostName(从节点IP); slaveConfig.setPort(6379); LettuceClientConfiguration clientConfig LettuceClientConfiguration.builder() .readFrom(ReadFrom.REPLICA_PREFERRED) .build(); return new LettuceConnectionFactory(masterConfig, slaveConfig, clientConfig); } }这里有几个关键点需要注意ReadFrom.REPLICA_PREFERRED表示优先从从节点读取Lettuce支持自动拓扑刷新当从节点变化时会自动感知连接池配置建议单独设置避免使用默认值3.2 RedisTemplate定制配置读写分离的RedisTemplateBean public RedisTemplateString, Object redisTemplate() { RedisTemplateString, Object template new RedisTemplate(); template.setConnectionFactory(redisConnectionFactory()); // 使用String序列化器 template.setKeySerializer(new StringRedisSerializer()); template.setValueSerializer(new GenericJackson2JsonRedisSerializer()); // 开启事务支持 template.setEnableTransactionSupport(true); return template; }4. 读写分离策略深度解析4.1 读策略配置选项Lettuce提供了多种读策略选择ReadFrom.MASTER // 强制从主节点读 ReadFrom.MASTER_PREFERRED // 优先主节点不可用时切从节点 ReadFrom.REPLICA // 只从从节点读 ReadFrom.REPLICA_PREFERRED // 优先从节点(推荐) ReadFrom.NEAREST // 读取延迟最低的节点在电商项目中我们选择了REPLICA_PREFERRED因为读操作占比高达90%可以容忍秒级数据延迟需要最大化减轻主节点压力4.2 写后读一致性处理一个常见问题是写入后立即读取可能获取不到最新数据。解决方案有对一致性要求高的操作指定从主节点读取// 使用Transactional确保读写都在主节点 Transactional public void updateAndGet(String key, String value) { redisTemplate.opsForValue().set(key, value); return redisTemplate.opsForValue().get(key); }使用Redis的WAIT命令强制同步redisTemplate.execute((RedisCallbackObject) connection - { connection.set(keySerializer.serialize(key), valueSerializer.serialize(value)); connection.waitForReplicas(1, 1000); // 等待至少1个从节点确认 return null; });5. 性能优化实战技巧5.1 连接池调优默认连接池配置往往不能满足生产需求建议这样调整Bean public LettuceConnectionFactory redisConnectionFactory() { // ...其他配置 GenericObjectPoolConfigObject poolConfig new GenericObjectPoolConfig(); poolConfig.setMaxTotal(200); // 最大连接数 poolConfig.setMaxIdle(50); // 最大空闲连接 poolConfig.setMinIdle(10); // 最小空闲连接 poolConfig.setMaxWait(Duration.ofMillis(1000)); // 获取连接超时时间 LettucePoolingClientConfiguration poolingConfig LettucePoolingClientConfiguration.builder() .poolConfig(poolConfig) .build(); return new LettuceConnectionFactory(masterConfig, poolingConfig); }5.2 监控指标采集通过Micrometer监控Redis指标Bean public MeterRegistryCustomizerMeterRegistry metricsCommonTags() { return registry - registry.config().commonTags( application, your-application-name); } // 在application.properties中开启指标 management.metrics.export.prometheus.enabledtrue关键监控指标包括redis.connections.activeredis.commands.latencyredis.replication.lag6. 生产环境踩坑实录6.1 主从切换问题我们曾经遇到主节点宕机后应用仍然向旧主节点发送写请求的情况。解决方案开启拓扑刷新LettuceClientConfiguration.builder() .clientOptions(ClientOptions.builder() .autoReconnect(true) .disconnectedBehavior(ClientOptions.DisconnectedBehavior.REJECT_COMMANDS) .build()) .commandTimeout(Duration.ofSeconds(1)) .build();配置哨兵模式实现自动故障转移RedisSentinelConfiguration sentinelConfig new RedisSentinelConfiguration() .master(mymaster) .sentinel(sentinel1, 26379) .sentinel(sentinel2, 26379);6.2 大key导致复制延迟某次大促期间一个10MB的缓存数据导致从节点复制延迟高达5秒。解决方法对大value进行拆分启用Redis的lazy free特性lazyfree-lazy-eviction yes lazyfree-lazy-expire yes监控复制延迟redis-cli info replication7. 高级特性扩展7.1 多级缓存架构对于超高并发场景可以结合本地缓存Cacheable(value users, key #userId) public User getUser(String userId) { // 先查本地缓存再查Redis }配置CacheManagerBean public CacheManager cacheManager() { CaffeineCacheManager caffeineCacheManager new CaffeineCacheManager(); caffeineCacheManager.setCaffeine(Caffeine.newBuilder() .expireAfterWrite(10, TimeUnit.MINUTES) .maximumSize(1000)); RedisCacheManager redisCacheManager RedisCacheManager.builder(redisConnectionFactory()) .cacheDefaults(RedisCacheConfiguration.defaultCacheConfig() .entryTtl(Duration.ofHours(1))) .build(); return new CompositeCacheManager(caffeineCacheManager, redisCacheManager); }7.2 读写分离分片架构当单主节点无法承受写压力时可以考虑分片Bean public RedisClusterConfiguration clusterConfiguration() { return new RedisClusterConfiguration() .clusterNode(节点1, 6379) .clusterNode(节点2, 6379); }这种架构下每个分片都有自己的主从结构既分散了写压力又保持了读扩展能力。
阅读完成 · 觉得有帮助?