8.3 Redis高可用方案

面试重要程度:⭐⭐⭐⭐⭐ 常见提问方式: 主从复制原理、哨兵选举机制、集群分片策略 预计阅读时间:50分钟

🎯 主从复制、哨兵模式、集群模式

主从复制原理

面试官: "Redis主从复制是如何工作的?全量同步和增量同步的区别是什么?"

必答要点:

复制流程详解:

/** * Redis主从复制三个阶段 */public class RedisReplication {        /**     * 第一阶段:建立连接     */    public void connectionPhase() {        /**         * 1. 从库执行 SLAVEOF master_ip master_port         * 2. 从库保存主库信息         * 3. 从库创建与主库的socket连接         * 4. 从库发送PING命令测试连接         * 5. 身份验证(如果设置了密码)         * 6. 从库发送端口信息给主库         */    }        /**     * 第二阶段:数据同步     */    public void dataSyncPhase() {        /**         * 全量同步(SYNC):         * 1. 从库发送PSYNC命令         * 2. 主库执行BGSAVE生成RDB文件         * 3. 主库将RDB文件发送给从库         * 4. 从库清空数据库,载入RDB文件         * 5. 主库将缓冲区的写命令发送给从库         */    }        /**     * 第三阶段:命令传播     */    public void commandPropagationPhase() {        /**         * 增量同步:         * 1. 主库执行写命令后,将命令发送给从库         * 2. 从库执行相同的写命令         * 3. 保持主从数据一致性         */    }}

PSYNC优化机制:

/** * Redis 2.8+ PSYNC命令优化 */public class PSyncOptimization {        /**     * 部分重同步机制     */    public void partialResync() {        /**         * 复制偏移量(replication offset):         * - 主库和从库都维护一个偏移量         * - 主库每传播N个字节,偏移量+N         * - 从库每接收N个字节,偏移量+N         *          * 复制积压缓冲区(replication backlog):         * - 主库维护的固定长度FIFO队列         * - 默认大小1MB         * - 保存最近传播的写命令         *          * 服务器运行ID(run ID):         * - 每个Redis服务器的唯一标识         * - 服务器启动时生成40位随机字符串         */                /**         * 部分重同步条件:         * 1. 从库记录的主库run ID与当前主库run ID相同         * 2. 从库的复制偏移量在复制积压缓冲区范围内         *          * 满足条件:执行部分重同步         * 不满足条件:执行全量同步         */    }}

哨兵模式(Sentinel)

面试官: "Redis哨兵是如何实现自动故障转移的?选举过程是怎样的?"

哨兵架构:

# sentinel.conf 配置示例port 26379sentinel monitor mymaster 127.0.0.1 6379 2sentinel down-after-milliseconds mymaster 5000sentinel parallel-syncs mymaster 1sentinel failover-timeout mymaster 10000​# 哨兵集群配置(至少3个节点)# sentinel1: 26379# sentinel2: 26380  # sentinel3: 26381

故障检测机制:

/** * 哨兵故障检测 */public class SentinelFailureDetection {        /**     * 主观下线(Subjectively Down)     */    public void subjectiveDown() {        /**         * 检测流程:         * 1. 哨兵每秒向主库发送PING命令         * 2. 如果超过down-after-milliseconds没有响应         * 3. 哨兵标记主库为主观下线(SDOWN)         *          * 注意:只是单个哨兵的判断,可能是网络问题         */    }        /**     * 客观下线(Objectively Down)     */    public void objectiveDown() {        /**         * 检测流程:         * 1. 哨兵发现主库主观下线后         * 2. 向其他哨兵发送SENTINEL is-master-down-by-addr命令         * 3. 如果超过quorum个哨兵认为主库下线         * 4. 标记主库为客观下线(ODOWN)         * 5. 开始故障转移流程         */    }}

选举和故障转移:

/** * 哨兵选举和故障转移 */public class SentinelElection {        /**     * 选举领导者哨兵     */    public void leaderElection() {        /**         * Raft算法选举:         * 1. 每个哨兵都可以成为候选者         * 2. 候选者向其他哨兵发送投票请求         * 3. 每个哨兵在一轮选举中只能投票给一个候选者         * 4. 获得超过半数选票的候选者成为领导者         * 5. 如果没有候选者获得半数选票,重新选举         */    }        /**     * 故障转移流程     */    public void failoverProcess() {        /**         * 1. 从从库中选择新的主库:         *    - 排除下线的从库         *    - 排除5秒内没有回复INFO命令的从库         *    - 排除与原主库断开连接超过10*down-after-milliseconds的从库         *    - 选择优先级最高的从库(replica-priority)         *    - 选择复制偏移量最大的从库(数据最新)         *    - 选择运行ID最小的从库         *          * 2. 将选中的从库升级为主库:         *    - 向从库发送SLAVEOF NO ONE命令         *          * 3. 修改其他从库的主库地址:         *    - 向其他从库发送SLAVEOF new_master_ip new_master_port         *          * 4. 更新客户端配置:         *    - 通过发布订阅机制通知客户端主库变更         */    }}

哨兵客户端实现:

/** * Spring Boot整合哨兵模式 */@Configurationpublic class RedisSentinelConfig {        @Bean    public LettuceConnectionFactory redisConnectionFactory() {        // 哨兵配置        RedisSentinelConfiguration sentinelConfig = new RedisSentinelConfiguration()            .master("mymaster")            .sentinel("127.0.0.1", 26379)            .sentinel("127.0.0.1", 26380)            .sentinel("127.0.0.1", 26381);                // 连接池配置        GenericObjectPoolConfig<Object> poolConfig = new GenericObjectPoolConfig<>();        poolConfig.setMaxTotal(20);        poolConfig.setMaxIdle(10);        poolConfig.setMinIdle(5);                LettucePoolingClientConfiguration clientConfig = LettucePoolingClientConfiguration.builder()            .poolConfig(poolConfig)            .build();                return new LettuceConnectionFactory(sentinelConfig, clientConfig);    }        @Bean    public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory connectionFactory) {        RedisTemplate<String, Object> template = new RedisTemplate<>();        template.setConnectionFactory(connectionFactory);                // 序列化配置        Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);        template.setDefaultSerializer(serializer);        template.setKeySerializer(new StringRedisSerializer());                return template;    }}​/** * 哨兵事件监听 */@Component@Slf4jpublic class SentinelEventListener {        @EventListener    public void handleMasterSwitched(RedisMasterReplicaChangedEvent event) {        log.info("主库切换事件: 旧主库={}:{}, 新主库={}:{}",             event.getOldMaster().getHost(), event.getOldMaster().getPort(),            event.getNewMaster().getHost(), event.getNewMaster().getPort());   

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

Java面试圣经 文章被收录于专栏

Java面试圣经,带你练透java圣经

全部评论
欢迎讨论
点赞 回复 分享
发布于 2025-09-06 11:27 江西

相关推荐

评论
点赞
1
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务