Redis主从复制深度解析

发布时间:2026/5/28 0:55:24

Redis主从复制深度解析 Redis主从复制深度解析引言Redis主从复制是实现数据高可用和读写分离的基础架构。通过主从复制主节点的数据可以同步到一个或多个从节点从而实现数据备份、读写分离和故障转移。本文将深入探讨Redis主从复制的工作原理、配置方法、故障处理以及最佳实践。主从复制原理1.1 复制机制概述Redis主从复制采用异步复制机制主节点将写操作记录在命令传播阶段同时将数据同步到从节点。┌──────────────────────────────────────────────────────────┐ │ 主节点 (Master) │ │ │ │ Client ──────► 写操作 ──────────► Command Queue │ │ │ │ │ ├──► Sync to Replicas │ │ │ │ │ ▼ │ │ Propagation │ │ │ │ └───────────────────────────┼─────────────────────────────┘ │ ┌───────────────────┼───────────────────┐ │ │ │ ▼ ▼ ▼ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ 从节点1 │ │ 从节点2 │ │ 从节点3 │ │ (Replica1) │ │ (Replica2) │ │ (Replica3) │ └──────────────┘ └──────────────┘ └──────────────┘1.2 复制过程# 在从节点执行复制命令 redis-cli REPLICAOF master-host master-port # 取消复制成为主节点 redis-cli REPLICAOF NO ONE主从配置2.1 从节点配置# redis.conf - 从节点配置 # 主节点配置 replicaof 192.168.1.100 6379 # 主节点认证密码如果有 masterauth password123 # 从节点只读模式 replica-read-only yes # 主节点不可用时的行为 # yes replica继续响应客户端使用过期数据 # no replica返回错误 replica-serve-stale-data yes # 同步配置 # 无盘同步推荐 repl-diskless-sync yes repl-diskless-sync-delay 5 # 流控配置 repl-backlog-size 10mb repl-backlog-ttl 3600 # 心跳配置 min-replicas-to-write 2 min-replicas-max-lag 5 # 禁止重启后执行全量同步 replica-lazy-flush no2.2 主节点配置# redis.conf - 主节点配置 # 监听地址 bind 0.0.0.0 # 端口 port 6379 # 认证密码可选 requirepass password123 # 最大连接从节点数 maxclients 10000 # 复制积压缓冲区大小 repl-backlog-size 10mb # 复制积压缓冲区生存时间 repl-backlog-ttl 3600 # 最小从节点数写入保证 min-replicas-to-write 2 min-replicas-max-lag 52.3 Java配置主从import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisStandaloneConfiguration; import org.springframework.data.redis.connection.jedis.JedisClientConfiguration; import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; Configuration public class RedisMasterSlaveConfig { Value(${redis.master.host}) private String masterHost; Value(${redis.master.port}) private int masterPort; Value(${redis.master.password:}) private String masterPassword; Value(${redis.slave.host:}) private String slaveHost; Value(${redis.slave.port:}) private int slavePort; Bean public RedisConnectionFactory connectionFactory() { RedisStandaloneConfiguration masterConfig new RedisStandaloneConfiguration(); masterConfig.setHostName(masterHost); masterConfig.setPort(masterPort); if (!masterPassword.isEmpty()) { masterConfig.setPassword(masterPassword); } GenericObjectPoolConfig? poolConfig new GenericObjectPoolConfig(); poolConfig.setMaxTotal(50); poolConfig.setMaxIdle(20); poolConfig.setMinIdle(5); JedisClientConfiguration clientConfig JedisClientConfiguration.builder() .usePooling() .poolConfig(poolConfig) .build(); return new JedisConnectionFactory(masterConfig, clientConfig); } Bean public RedisTemplateString, Object redisTemplate( RedisConnectionFactory connectionFactory) { RedisTemplateString, Object template new RedisTemplate(); template.setConnectionFactory(connectionFactory); // 配置序列化 template.setKeySerializer( new StringRedisSerializer()); template.setValueSerializer( new GenericJackson2JsonRedisSerializer()); template.setHashKeySerializer( new StringRedisSerializer()); template.setHashValueSerializer( new GenericJackson2JsonRedisSerializer()); template.afterPropertiesSet(); return template; } }读写分离3.1 读写分离配置import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.connection.RedisReadFrom; import org.springframework.data.redis.connection.RedisReplicaConfiguration; import org.springframework.data.redis.connection.RedisStandaloneConfiguration; import org.springframework.data.redis.connection.jedis.JedisClientConfiguration; import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; import org.springframework.stereotype.Component; Component public class RedisReadWriteSplitting { Autowired private JedisConnectionFactory masterFactory; Autowired private JedisConnectionFactory slaveFactory; /** * 读取操作使用从节点 */ public String read(String key) { RedisConnection slaveConnection slaveFactory.getConnection(); try { byte[] value slaveConnection.stringCommands().get( key.getBytes()); return value ! null ? new String(value) : null; } finally { slaveConnection.close(); } } /** * 写入操作使用主节点 */ public void write(String key, String value) { RedisConnection masterConnection masterFactory.getConnection(); try { masterConnection.stringCommands().set( key.getBytes(), value.getBytes()); } finally { masterConnection.close(); } } /** * 使用模板进行读写分离 */ public void performReadWriteOperations() { // 写入主节点 // redisTemplate.opsForValue().set(key, value); // 从从节点读取 // slaveTemplate.opsForValue().get(key); } }3.2 动态路由配置import org.springframework.stereotype.Component; import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.core.RedisTemplate; import java.util.Random; Component public class DynamicRedisRouter { Autowired private RedisTemplateString, String masterTemplate; Autowired private RedisTemplateString, String slaveTemplate1; Autowired private RedisTemplateString, String slaveTemplate2; private final Random random new Random(); /** * 读取操作随机选择从节点 */ public String read(String key) { RedisTemplateString, String[] slaves {slaveTemplate1, slaveTemplate2}; int index random.nextInt(slaves.length); return slaves[index].opsForValue().get(key); } /** * 写入操作主节点 */ public void write(String key, String value) { masterTemplate.opsForValue().set(key, value); } /** * 批量读取使用所有从节点 */ public MapString, String batchRead(ListString keys) { MapString, String results new HashMap(); for (RedisTemplateString, String slave : new RedisTemplate[]{slaveTemplate1, slaveTemplate2}) { ListString values slave.opsForValue().multiGet(keys); for (int i 0; i keys.size(); i) { if (values.get(i) ! null) { results.put(keys.get(i), values.get(i)); } } } return results; } }故障处理4.1 自动故障转移# 查看主从状态 redis-cli INFO replication # 主节点故障时从节点自动提升为主节点 # 需要配合Sentinel或Cluster使用 # 手动故障转移 redis-cli FAILOVER4.2 故障恢复import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.connection.RedisServerCommands; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; Component public class ReplicationFailureHandler { Autowired private RedisTemplateString, String masterTemplate; Autowired private RedisTemplateString, String slaveTemplate; /** * 检测主节点状态 */ public boolean isMasterAvailable() { try { RedisConnection connection masterTemplate.getConnectionFactory().getConnection(); String role connection.serverCommands().debugObject( key).toString(); connection.close(); return true; } catch (Exception e) { return false; } } /** * 检测从节点状态 */ public boolean isSlaveAvailable() { try { RedisConnection connection slaveTemplate.getConnectionFactory().getConnection(); String role connection.serverCommands().debugObject( key).toString(); connection.close(); return true; } catch (Exception e) { return false; } } /** * 执行故障转移 */ public void performFailover() { // 1. 检测主节点 if (isMasterAvailable()) { System.out.println(Master is available, no failover needed); return; } // 2. 停止从节点复制 slaveTemplate.getConnectionFactory().getConnection() .slaveOfNoOne(); // 3. 更新配置 System.out.println(Failover completed, slave promoted to master); } /** * 主节点恢复后恢复主从关系 */ public void restoreReplication(String masterHost, int masterPort) { slaveTemplate.getConnectionFactory().getConnection() .slaveOf(masterHost.getBytes(), masterPort); System.out.println(Replication restored to masterHost : masterPort); } }复制延迟监控5.1 延迟监控import org.springframework.data.redis.core.RedisTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.util.*; Component public class ReplicationLagMonitor { Autowired private RedisTemplateString, String masterTemplate; Autowired private RedisTemplateString, String slaveTemplate; /** * 获取复制延迟毫秒 */ public long getReplicationLag() { // 在主节点记录时间戳 String key replication:lag:test: System.currentTimeMillis(); masterTemplate.opsForValue().set(key, String.valueOf(System.currentTimeMillis())); // 在从节点读取并计算延迟 try { Thread.sleep(10); // 等待复制 String value slaveTemplate.opsForValue().get(key); if (value ! null) { long masterTime Long.parseLong(value); long currentTime System.currentTimeMillis(); return currentTime - masterTime; } } catch (Exception e) { return -1; } return -1; } /** * 获取复制详细信息 */ public MapString, Object getReplicationInfo() { MapString, Object info new HashMap(); // 主节点信息 info.put(masterHost, localhost); info.put(masterPort, 6379); info.put(connectedSlaves, getConnectedSlavesCount()); // 从节点信息 info.put(slaveHost, localhost); info.put(slavePort, 6380); info.put(slaveLag, getReplicationLag()); info.put(slaveSyncStatus, getSlaveSyncStatus()); return info; } private int getConnectedSlavesCount() { // 从主节点获取连接的从节点数量 return 2; } private String getSlaveSyncStatus() { // 获取从节点同步状态 return connected; } }5.2 延迟告警import org.springframework.scheduling.annotation.Scheduled; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; Component public class ReplicationLagAlert { Autowired private ReplicationLagMonitor monitor; private static final long LAG_THRESHOLD_MS 1000; Scheduled(fixedDelay 60000) // 每分钟检查一次 public void checkReplicationLag() { long lag monitor.getReplicationLag(); if (lag LAG_THRESHOLD_MS) { sendAlert(lag); } } private void sendAlert(long lag) { // 发送告警通知 System.out.println(WARNING: Replication lag exceeds threshold! Current lag: lag ms); // 可以集成邮件、短信、企业微信等通知方式 // emailService.sendAlert(Replication lag: lag); // smsService.sendAlert(Replication lag: lag); } }最佳实践6.1 配置最佳实践# 主从复制最佳配置 # 主节点配置 bind 0.0.0.0 port 6379 requirepass master_password maxmemory 10gb maxmemory-policy allkeys-lru # 复制配置 repl-diskless-sync yes repl-diskless-sync-delay 5 repl-backlog-size 64mb repl-backlog-ttl 3600 # 写入保证 min-replicas-to-write 2 min-replicas-max-lag 5 # 从节点配置 replicaof 192.168.1.100 6379 masterauth master_password replica-read-only yes replica-serve-stale-data yes repl-diskless-sync yes repl-backlog-size 64mb # 网络优化 tcp-keepalive 3006.2 性能优化public class ReplicationPerformanceOptimization { /** * 优化复制性能 */ public void optimizeReplication() { // 1. 启用无盘同步 // repl-diskless-sync yes // 2. 增大复制积压缓冲区 // repl-backlog-size 64mb // 3. 配置适当的从节点数量 // 根据业务需求和从节点处理能力来决定 // 4. 使用压缩 // replcompression yes // 5. 优化网络 // tcp-keepalive 300 } /** * 批量操作优化 */ public void optimizeBatchOperations() { // 使用pipeline减少网络往返 // redisTemplate.executePipelined() // 使用MULTI/EXEC事务 // redisTemplate.execute(new SessionCallbackListObject() { // Override // public ListObject execute(RedisOperations operations) // throws DataAccessException { // operations.multi(); // operations.opsForValue().set(key1, value1); // operations.opsForValue().set(key2, value2); // return operations.exec(); // } // }); } }6.3 监控方案import io.micrometer.core.instrument.MeterRegistry; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; Component public class ReplicationMetricsCollector { Autowired private ReplicationLagMonitor lagMonitor; Autowired private MeterRegistry meterRegistry; Scheduled(fixedDelay 60000) public void collectMetrics() { // 收集复制延迟指标 long lag lagMonitor.getReplicationLag(); meterRegistry.gauge(redis.replication.lag, lag); // 收集连接状态指标 MapString, Object info lagMonitor.getReplicationInfo(); meterRegistry.gauge(redis.slaves.connected, (Integer) info.get(connectedSlaves)); // 收集其他复制指标 meterRegistry.gauge(redis.replication.backlog_size, getBacklogSize()); } private long getBacklogSize() { // 从Redis获取backlog大小 return 0; } }数据一致性7.1 一致性保证public class ConsistencyGuarantees { /** * 强一致性写入 */ public void strongConsistencyWrite(String key, String value) { // 使用WAIT命令等待复制确认 // redisTemplate.execute((RedisCallbackLong) connection - // connection.commands().waitForReplication(2, 5000)); // 执行写操作 // redisTemplate.opsForValue().set(key, value); } /** * 最终一致性读取 */ public String eventuallyConsistentRead(String key) { // 直接从从节点读取 // return slaveTemplate.opsForValue().get(key); // 或者从主节点读取以保证最新数据 // return masterTemplate.opsForValue().get(key); return null; } /** * 读写分离策略 */ public enum ReadStrategy { ALWAYS_MASTER, // 始终从主节点读取 ALWAYS_SLAVE, // 始终从从节点读取 PREFER_SLAVE, // 优先从从节点读取 RANDOM // 随机选择 } }7.2 一致性级别选择public class ConsistencyLevelSelector { public void selectConsistencyLevel(ConsistencyLevel level) { switch (level) { case STRONG: // 所有操作都在主节点执行 // 使用WAIT命令等待复制确认 break; case BOUNDED_STALENESS: // 使用从节点但限制延迟 // 检查复制延迟超过阈值则从主节点读取 break; case EVENTUAL: // 可以使用从节点 // 接受可能的数据不一致 break; case READ_YOUR_WRITES: // 写入后立即读取自己的数据 // 从主节点读取 break; } } public enum ConsistencyLevel { STRONG, // 强一致性 BOUNDED_STALENESS, // 有界最终一致性 EVENTUAL, // 最终一致性 READ_YOUR_WRITES // 读己之写一致性 } }总结Redis主从复制是构建高可用Redis架构的基础。通过合理配置主从复制可以实现数据备份、读写分离和故障转移。在实际应用中需要根据业务需求选择合适的复制模式并建立完善的监控告警机制确保主从复制的稳定运行。同时需要注意数据一致性问题在性能和一致性之间做出合理权衡。

相关新闻