介绍一下Redis的高可用方案

主从复制(基础高可用)✅

这是所有高可用方案的基石,核心解决数据备份读写分离问题。

核心架构

image

工作原理

  1. 从节点启动后,向主节点发送PSYNC命令
  2. 首次连接:主节点执行全量复制(生成 RDB 文件发送给从节点)
  3. 后续连接:主节点执行增量复制(只发送偏移量之后的命令)

优缺点

  • ✅ 优点:实现简单,读能力线性提升,数据有备份
  • ❌ 缺点:不能自动故障转移,主节点挂了需要手动切换;单主写能力瓶颈

适用场景

  • 测试环境、小流量业务(QPS < 1000)
  • 只需要读写分离和数据备份的场景

哨兵模式(Sentinel)🛡️

在主从复制基础上,解决自动故障转移问题,是中小流量生产环境最常用的方案。

三大核心功能

  1. 监控:持续检测主从节点是否正常运行
  2. 选主:主节点故障时,自动从从节点中选举新主
  3. 通知:将故障转移结果通知客户端和其他节点

核心架构

image

故障转移流程

  1. 单个哨兵发现主节点不可达 → 主观下线
  2. 超过半数哨兵确认主节点不可达 → 客观下线
  3. 哨兵集群选举出一个领导者哨兵
  4. 领导者哨兵从从节点中选举新主节点
  5. 更新配置,通知客户端,旧主节点变为从节点

优缺点

  • ✅ 优点:自动故障转移,高可用,运维简单
  • ❌ 缺点:不能水平扩展,单主写能力瓶颈;哨兵集群本身也需要高可用

适用场景

  • 中小流量生产环境(QPS < 10000)
  • 写压力不大,读压力较大的业务

Redis Cluster 集群模式(分片高可用)⚡

解决单节点写能力瓶颈水平扩展问题,是大流量生产环境的标准方案。

核心概念:16384 个哈希槽

  • Redis Cluster 将所有数据映射到 16384 个哈希槽中
  • 每个主节点负责一部分哈希槽
  • 客户端通过CRC16(key) % 16384计算 key 所在的槽,直接连接对应节点

核心架构

image

故障转移机制

  • 每个节点都监控其他节点的状态
  • 当某个主节点故障时,它的从节点会自动晋升为新主节点
  • 集群自动更新哈希槽映射,客户端通过MOVED重定向到新节点

优缺点

  • ✅ 优点:线性水平扩展,写能力和读能力都能线性提升;自动故障转移
  • ❌ 缺点:运维复杂;不支持跨槽的多键操作(如MGET、MSET);数据迁移有短暂影响

适用场景

  • 大流量高并发生产环境(QPS > 10000)
  • 数据量超过单节点内存容量的业务

三大核心方案对比表 📊

方案 最小节点数 故障转移 水平扩展 写能力 读能力 运维复杂度 适用场景
主从复制 2 ❌ 手动 ❌ 不支持 单主瓶颈 线性提升 测试环境、小流量
哨兵模式 5(3 哨兵 + 2 主从) ✅ 自动 ❌ 不支持 单主瓶颈 线性提升 中小流量生产
Redis Cluster 6(3 主 3 从) ✅ 自动 ✅ 线性扩展 线性提升 线性提升 大流量高并发

企业级进阶优化与云原生方案 💡

  1. 读写分离优化:使用客户端分片或代理(如 Twemproxy、Codis)实现更灵活的读写分离
  2. 持久化策略:主节点关闭 RDB,从节点开启 RDB+AOF 混合持久化,平衡性能和数据安全
  3. 慢查询监控:定期清理慢查询日志,设置合理的超时时间
  4. 云原生方案:在 K8s 环境中使用 Redis Operator(如 Redis Cluster Operator),实现自动化部署、扩缩容和故障转移
  5. 云厂商托管:对于核心业务,建议使用阿里云 Redis、腾讯云 Redis 等托管服务,省心又稳定

面试加分总结 ✨

在实际项目中,我一般会根据业务阶段和流量规模来选择合适的方案:

  1. 项目初期:主从复制 + 哨兵,运维简单,成本低
  2. 业务增长期:当单主写能力达到瓶颈时,迁移到 Redis Cluster
  3. 云原生环境:优先使用 Redis Operator,自动化运维效率更高
  4. 核心业务:使用云厂商托管 Redis,提供 99.99% 以上的可用性

核心代码与技术亮点实现 💻

1 Jedis 连接哨兵集群(生产级配置)

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisSentinelPool;
import java.util.HashSet;
import java.util.Set;

public class RedisSentinelConfig {
    // 技术亮点1:单例模式+双重检查锁,保证连接池唯一
    private static volatile JedisSentinelPool jedisSentinelPool;

    public static JedisSentinelPool getJedisSentinelPool() {
        if (jedisSentinelPool == null) {
            synchronized (RedisSentinelConfig.class) {
                if (jedisSentinelPool == null) {
                    // 哨兵节点地址集合
                    Set sentinels = new HashSet<>();
                    sentinels.add("192.168.1.10:26379");
                    sentinels.add("192.168.1.11:26379");
                    sentinels.add("192.168.1.12:26379");

                    // 技术亮点2:精细化连接池配置,避免连接泄漏
                    jedisSentinelPool = new JedisSentinelPool(
                            "mymaster", // 主节点名称
                            sentinels,
                            "password123", // Redis密码
                            2000, // 连接超时
                            100, // 最大连接数
                            10, // 最小空闲连接数
                            30000 // 连接最大空闲时间
                    );
                }
            }
        }
        return jedisSentinelPool;
    }

    // 使用示例
    public static void main(String[] args) {
        try (Jedis jedis = getJedisSentinelPool().getResource()) {
            jedis.set("key", "value");
            System.out.println(jedis.get("key"));
        }
    }
}

2 Spring Boot 集成 Redis Cluster(Lettuce 客户端)

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisClusterConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisClusterConfig {
    @Bean
    public LettuceConnectionFactory redisConnectionFactory() {
        RedisClusterConfiguration clusterConfig = new RedisClusterConfiguration();
        // 集群节点地址
        clusterConfig.clusterNode("192.168.1.20", 6379);
        clusterConfig.clusterNode("192.168.1.21", 6379);
        clusterConfig.clusterNode("192.168.1.22", 6379);
        clusterConfig.setPassword("password123");
        // 技术亮点3:开启集群重定向自动跟随
        clusterConfig.setMaxRedirects(3);

        return new LettuceConnectionFactory(clusterConfig);
    }

    @Bean
    public RedisTemplate redisTemplate(LettuceConnectionFactory connectionFactory) {
        RedisTemplate template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);
        // 技术亮点4:使用String序列化器,避免默认JDK序列化的乱码问题
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(new StringRedisSerializer());
        template.afterPropertiesSet();
        return template;
    }
}

3 技术亮点:自定义 Redis Cluster 哈希标签路由

解决 Redis Cluster 不支持跨槽多键操作的核心方案

/**
 * 技术亮点:自定义哈希标签工具类
 * 将相同业务的key强制路由到同一个哈希槽,支持MGET/MSET等多键操作
 */
public class RedisHashTagUtil {
    // 哈希标签分隔符
    private static final String HASH_TAG_START = "{";
    private static final String HASH_TAG_END = "}";

    /**
     * 生成带哈希标签的key
     * @param businessId 业务ID(如用户ID、订单ID)
     * @param keySuffix key后缀
     * @return 带哈希标签的key
     */
    public static String generateKey(String businessId, String keySuffix) {
        return HASH_TAG_START + businessId + HASH_TAG_END + ":" + keySuffix;
    }

    // 使用示例
    public static void main(String[] args) {
        // 同一个用户的所有key都会路由到同一个哈希槽
        String userInfoKey = generateKey("user123", "info");
        String userOrderKey = generateKey("user123", "orders");
        String userCartKey = generateKey("user123", "cart");

        // 现在可以安全地执行MGET操作
        // redisTemplate.opsForValue().multiGet(Arrays.asList(userInfoKey, userOrderKey, userCartKey));
    }
}

4 技术亮点:主从切换时的客户端重试机制

import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Component;
import redis.clients.jedis.exceptions.JedisConnectionException;

@Component
public class RedisRetryService {
    /**
     * 技术亮点:使用Spring Retry实现主从切换时的自动重试
     * 当发生连接异常时,最多重试3次,每次间隔1秒
     */
    @Retryable(
            value = JedisConnectionException.class,
            maxAttempts = 3,
            backoff = @Backoff(delay = 1000, multiplier = 2)
    )
    public String getWithRetry(String key) {
        return redisTemplate.opsForValue().get(key);
    }
}

技术难点与解决方案汇总 🛠️

技术难点 问题描述 根本原因 解决方案
主从复制数据不一致 从节点数据比主节点旧,读取到脏数据 主从同步有延迟;网络抖动导致增量复制中断 1. 监控主从延迟(info replication中的lag字段)2. 关键业务强制读主节点3. 开启repl-disable-tcp-nodelay减少延迟
哨兵模式脑裂问题 网络分区导致出现两个主节点,数据丢失 哨兵集群无法达成共识;旧主节点恢复后继续接收写请求 1. 配置min-replicas-to-write 1(至少 1 个从节点确认才允许写)2. 配置min-replicas-max-lag 10(从节点延迟超过 10 秒拒绝写)3. 哨兵节点数量必须为奇数,且至少 3 个
Redis Cluster 跨槽操作 执行MGET/MSET/ 事务时抛出CrossSlot异常 不同 key 映射到不同哈希槽,集群不支持跨槽操作 1. 使用哈希标签将相关 key 路由到同一个槽(推荐)2. 客户端拆分多键操作,分别执行后合并结果3. 使用代理层(如 Codis)自动处理跨槽操作
大集群数据迁移 扩容 / 缩容时数据迁移慢,影响业务 全量复制 RDB 文件大;迁移过程中阻塞节点 1. 采用增量迁移方式,先迁移全量数据,再同步增量命令2. 低峰期执行迁移操作3. 限制迁移速度(cluster-migration-barrier)
主从切换请求丢失 主节点故障时,未同步到从节点的写请求丢失 Redis 异步复制机制,主节点写成功后立即返回客户端 1. 开启WAIT命令,等待至少 N 个从节点确认后再返回2. 业务层实现幂等性,允许重复请求3. 核心业务使用强一致性方案(如 Redis 7.0 的 WAITAOF)
缓存雪崩 大量缓存同时过期,数据库压力骤增 缓存过期时间设置相同;Redis 集群整体宕机 1. 给缓存过期时间添加随机值(±5 分钟)2. 搭建 Redis 集群保证高可用3. 服务降级和熔断机制

面试加分总结 ✨

以上就是我对 Redis 高可用方案的完整理解。在实际项目中,我不仅掌握了这四种核心方案的原理和部署,还解决了很多生产环境中的实际问题,比如脑裂问题、跨槽操作问题主从切换数据丢失问题
我认为 Redis 高可用的核心不是简单地搭建集群,而是根据业务特点选择合适的方案,并做好监控和应急预案。比如对于金融支付业务,我会优先保证数据一致性,开启WAIT命令和强一致复制;对于电商秒杀业务,我会优先保证性能,使用 Redis Cluster 分片集群,并做好缓存降级。

🎤 面试现场实录

🙋‍♂️ 面试官:你好,今天我们聊一道场景设计题:介绍一下你对 Redis 高可用方案的理解,以及你们项目中是怎么用的? 不用紧张,想到哪说到哪,我来顺着问。

🧱 主从复制 —— 地基

👨‍💻 候选人:

好的,我从最基础的开始讲。如果只部署单个 Redis,它一旦挂了业务就全停,所以第一步我们一定会做主从复制

架构很简单:一个 Master 负责处理写请求,挂一或多个 Slave,Slave 主要负责读请求,同时异步从 Master 同步数据。这样至少数据有了多副本,不至于单点丢失。

🙋‍♂️ 面试官:

嗯,那这算高可用吗?

👨‍💻 候选人:

严格来说不算,它只是数据冗余。因为 Master 挂了之后,Slave 没法自动接管,需要手动敲 SLAVEOF NO ONE 命令把一台从库提成主库。这个过程运维介入慢,业务中断时间长。所以主从复制只是地基,高可用得在这个基础上再加东西。

image

🙋‍♂️ 面试官:

明白,那你们怎么解决自动切换的问题?

🛡️ 哨兵 Sentinel —— 自动容灾

👨‍💻 候选人:

我们在主从之上部署哨兵集群。哨兵本质上也是一个 Redis 进程,只是它不存数据,专门盯着主节点和从节点的健康状态。

整个机制我分几步说:

  1. 监控:每个哨兵定期 PING 主从节点。
  2. 主观下线:如果一个哨兵发现主节点 PING 不通了,会认为它“主观下线”。
  3. 客观下线:为了避免网络抖动误判,必须达到配置的 quorum 数量(比如3个哨兵里至少2个)都认为它挂了,才会真正判定“客观下线”,触发故障转移。
  4. 选举 Leader:哨兵集群内部用类似 Raft 的机制选出一个哨兵领导者来执行切换。
  5. 切换:Leader 从健康的从库中选一个提升为新主,改配置,然后通知其他从库去复制新主。最关键的是,它还会通过 Pub/Sub 机制通知客户端新主地址。

这样,整个切换过程几秒到十几秒,运维完全不用干预。我们很多中小型项目,用哨兵方案就足够了。😊

image

🙋‍♂️ 面试官:

不错,那你遇到过脑裂的问题吗?怎么解决的?

👨‍💻 候选人:

遇到过,这是个经典坑。比如网络分区,旧主其实没死,只是和哨兵失联了,哨兵选出了新主,这时就存在两个 Master 同时接收写入。等网络恢复,旧主的数据会被覆盖丢掉。

我们的解决办法是在 Master 上配置两个参数:

  • min-replicas-to-write 1:至少要有1个从库连接。
  • min-replicas-max-lag 10:从库复制延迟不能超过10秒。

如果达不到这个条件,旧主就拒绝写入,这样它自己就发现“我可能被孤立了”,主动放弃写入,避免脑裂数据冲突。虽然会牺牲一部分可用性,但保证了数据一致。🛡️

🙋‍♂️ 面试官:

那对于异步复制导致的数据丢失,你们怎么看?

👨‍💻 候选人:

确实,主挂时还没来得及传给从库的数据就丢了。我们一般这样取舍:如果对一致性要求极高(比如库存扣减),就用 Redis 的 WAIT 命令强制同步复制一定数量的从库再返回,但这会增加延迟。大部分缓存场景我们接受少量丢失,靠持久化兜底,业务上做好最终一致性的补偿逻辑。

🌐 Redis Cluster —— 海量数据与流量

🙋‍♂️ 面试官:

哨兵方案听起来很成熟了,那为什么后面还要用 Redis Cluster?

👨‍💻 候选人:因为哨兵只能解决高可用,但解决不了数据量大****和写流量高的问题。所有写请求都还压在一个 Master 上,存储也受单机内存限制。当数据量上了百 G、TPS 上万,哨兵方案就吃力了,这时就需要 Redis Cluster,它既高可用,又能水平扩展

核心设计我讲三点:

  • 分片:把整个数据空间划分成 16384 个哈希槽(slot),每个主节点负责一部分槽。Key 通过 CRC16(key) % 16384 确定属于哪个槽,然后去对应节点处理。
  • 去中心化:所有节点之间用 Gossip 协议互相交换状态,没有中心代理,每个节点都知道整个集群的拓扑。
  • 内建故障转移:每个主节点可以挂至少一个从节点。当某个主挂了,它的从节点会发起选举(由集群中其他主节点投票),晋升为新主,这个过程不依赖任何外部哨兵,集群自己搞定。

image

🙋‍♂️ 面试官:

那客户端怎么知道去哪个节点找数据?

👨‍💻 候选人:

这就要用智能客户端,像 Java 里的 LettuceJedisCluster。它们会缓存槽与节点的映射关系,计算好槽后直接连目标节点。如果因为扩缩容槽迁移了,节点会返回 MOVED 错误码和新节点地址,客户端会自动更新映射并重试,对业务几乎透明。👍

🙋‍♂️ 面试官:

Cluster 有什么限制?

👨‍💻 候选人:

最主要的是多 Key 操作限制,比如 mget、事务,要求所有 Key 必须在同一个槽。我们可以用 {} 强制让部分 Key 落到同一槽,但不能滥用。另外跨节点的事务不支持,所以我们会在设计 Key 时尽量规避这种需求。

📊 对比与选型

🙋‍♂️ 面试官:

最后你帮我总结一下,什么时候选什么方案?

👨‍💻 候选人:

我画个表吧,一目了然:

方案 自动故障转移 写扩展 运维复杂度 适合场景
主从 ❌ 手动 测试、对可用性无要求
哨兵 Sentinel ✅ 自动 ❌ (只有读扩展) ⭐⭐ 中小型项目首选,数据量不大但必须自动容灾
Cluster ✅ 自动 ✅ (读写都可扩展) ⭐⭐⭐ 大流量/海量数据,互联网核心链路

我们一般的路径是:项目初期用哨兵,等数据量或写入压力上来了,再平滑迁移到 Cluster。迁移前会在备集群先建好 Cluster,用双写或同步工具切流。

☕ Java 研发配合点

🙋‍♂️ 面试官:

很好,最后说下你们 Java 客户端怎么配合这些方案?

👨‍💻 候选人:

  • 哨兵模式:我们用 LettuceRedisSentinelClientJedisSentinelPool,只需配置哨兵地址,框架自动发现并连接主库,主从切换时客户端能收到通知并重连。
  • Cluster 模式:直接用 Spring Boot 默认的 Lettuce,开启 spring.redis.cluster.nodes 配置,它会自动处理 MOVEDASK 重定向,底层长连接会跟 Gossip 拓扑变化动态刷新。
  • 另外,不管哪种模式,我们都会开启 RDB + AOF 混合持久化,保证节点重启后能快速恢复数据,避免空节点加入集群导致数据迁移的灾难。🔥

💻 核心代码 & 技术亮点

🙋‍♂️ 面试官:

刚才你提到用 Lettuce 连接哨兵和 Cluster,能挑几个关键代码段展示一下吗?主要看看你们的连接配置和故障转移时的处理。

👨‍💻 候选人:

没问题,我先贴一段生产环境简化版,用 Spring Boot + Lettuce 连接 哨兵模式 的配置和自愈处理。

1️⃣ 哨兵模式:自动发现主库、切换重连

@Configuration
public class RedisSentinelConfig {

    @Bean
    public LettuceConnectionFactory lettuceConnectionFactory() {
        RedisSentinelConfiguration sentinelConfig = 
            new RedisSentinelConfiguration()
                .master("mymaster")                     // 哨兵监控的主库名
                .sentinel("192.168.1.10", 26379)
                .sentinel("192.168.1.11", 26379)
                .sentinel("192.168.1.12", 26379);
        sentinelConfig.setPassword(RedisPassword.of("sentinel_pass"));
        
        LettuceClientConfiguration clientConfig = 
            LettuceClientConfiguration.builder()
                .readFrom(ReadFrom.REPLICA_PREFERRED)   // 优先从副本读,分担压力
                .commandTimeout(Duration.ofSeconds(2))
                .build();
                
        return new LettuceConnectionFactory(sentinelConfig, clientConfig);
    }

    @Bean
    public RedisTemplate redisTemplate(
            LettuceConnectionFactory factory) {
        RedisTemplate template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        return template;
    }
}

🔍 技术亮点:

  • ReadFrom.REPLICA_PREFERRED 让读请求优先落到从库,仅当从库不可用时才读主库,实现读写分离且不丢可用性。
  • 只需配置哨兵节点,客户端在背后会自动订阅哨兵频道,主从切换后 1~3 秒内就能刷新拓扑,完全无感。
  • commandTimeout 设得短一点,防止在故障切换时请求长时间卡死。

2️⃣ Cluster 模式:智能重定向 & 管道优化

@Configuration
public class RedisClusterConfig {

    @Bean
    public LettuceConnectionFactory clusterConnectionFactory() {
        RedisClusterConfiguration clusterConfig = 
            new RedisClusterConfiguration(Arrays.asList(
                "192.168.2.10:6379",
                "192.168.2.11:6379",
                "192.168.2.12:6379"
            ));
        clusterConfig.setPassword("cluster_pass");
        // 开启自适应刷新,集群拓扑变化时自动感知
        ClusterTopologyRefreshOptions refreshOptions = 
            ClusterTopologyRefreshOptions.builder()
                .enablePeriodicRefresh(Duration.ofSeconds(30))  // 每30秒检查一次
                .enableAllAdaptiveRefreshTriggers()             // 检测到MOVED/ASK等自动立即刷新
                .build();
        
        LettuceClientConfiguration clientConfig = 
            LettuceClientConfiguration.builder()
                .clientOptions(ClusterClientOptions.builder()
                    .topologyRefreshOptions(refreshOptions)
                    .build())
                .build();
                
        return new LettuceConnectionFactory(clusterConfig, clientConfig);
    }
}

🔍 技术亮点:

  • ClusterTopologyRefreshOptions 是生产必备,避免槽迁移时客户端长时间拿着过期映射,导致大量 MOVED 重定向增加延迟。
  • enableAllAdaptiveRefreshTriggers() 会在收到重定向错误或节点角色变更时立即刷新拓扑,配合 30 秒定期刷新,双保险。
  • 连接池天然支持每个分片内部的主从自动切换,代码零改动。

3️⃣ 业务层防御性编码:故障时降级

@Service
public class CacheService {

    @Autowired
    private RedisTemplate redisTemplate;

    public Optional getUserById(String id) {
        try {
            User user = (User) redisTemplate.opsForValue().get("user:" + id);
            return Optional.ofNullable(user);
        } catch (RedisConnectionFailureException e) {
            log.warn("Redis 不可用,降级查 DB", e);
            // 降级到数据库查询
            return Optional.ofNullable(userDao.findById(id));
        } catch (Exception e) {
            log.error("Redis 异常", e);
            return Optional.empty();
        }
    }
}

🔍 技术亮点:

  • 捕获 RedisConnectionFailureException,这是 Lettuce 在连接断开或集群不可达时抛出的,防止缓存雪崩
  • 降级查 DB 并打印监控日志,保证核心链路可用。
  • 结合 Hystrix/Sentinel 熔断,能在 Redis 大面积故障时快速失败,保护整体系统。😌

🧨 技术难点与解决方案

🙋‍♂️ 面试官:

代码挺扎实,那你觉得实现 Redis 高可用最大的技术难点有哪些?你们是怎么一个一个啃下来的?

👨‍💻 候选人:

我梳理了四个生产环境下最头疼的点,以及对应的解法。

难点 详细描述 我们的解决方案
🧠 脑裂(Split-Brain) 哨兵模式网络分区,旧主被孤立却继续接写入,恢复后数据被新主覆盖 ① 主节点设置 min-replicas-to-writemin-replicas-max-lag,当没有足够从库同步时,旧主直接拒绝写入。② 客户端收到异常后触发告警并降级,人工确认修复。
📉 异步复制数据丢失 主宕机前瞬间写入的数据,还没来得及复制到从库就丢失了 ① 核心业务(如金融订单)使用 WAIT 命令,强制同步到指定数量的从库后才返回成功,牺牲部分延迟换取一致性。② 结合 AOF 持久化 appendfsync everysec,重启后能从磁盘恢复绝大部分数据。
🔄 故障切换期间的“短暂不可用” 哨兵选举、提升新主期间(2~10秒),所有写请求都会失败 ① 客户端侧开启重试机制,Lettuce 默认会重试失效节点,配合指数退避。② 业务层加本地缓存(Caffeine) 兜底,读请求可容忍几秒旧数据。③ 监控切换耗时,及时优化哨兵参数(sentinel down-after-milliseconds 不宜过大)。
⚡ 数据倾斜与热点 Key Cluster 中某些槽的数据量或访问量远超其他节点,导致单分片压力过高 ① 对热点 Key 做 多副本打散,比如 hot:key:{hash} 分散到多个 Key,本地随机取一个。② 业务层先通过监控发现后,主动调整 Key 设计,使用 {} 将互相访问频繁的 Key 规范到同一槽,但避免大 Key 聚集。③ 必要时升级分片规格或垂直扩容该分片。
🌀 集群扩容槽迁移影响延迟 迁移过程中,客户端会遇到 ASK 重定向,频繁的重定向导致毛刺 ① 打开客户端自适应拓扑刷新,减少重复的 ASK 请求。② 分批次、低峰期迁移,控制 migrate 的 pipeline 速度。③ 对延迟敏感业务,迁移时手动临时切换读取从节点,避免主节点阻塞。

🙋‍♂️ 面试官:

最后一个问题,你觉得这些难点里,哪一个是最容易被开发者忽略,但又最致命的

👨‍💻 候选人:

绝对是脑裂。因为很多团队配置哨兵或者 Cluster 后,看到能自动切换就以为万事大吉了。脑裂一旦发生,在没有 min-replicas-to-write 保护的情况下,丢失的是切换期间所有写入数据,而且是静默丢失,业务侧完全感知不到,直到对账才发现。所以我们规定,所有线上 Redis 主节点必须配上这些反脑裂参数,否则上线审批不通过。🚫

🙋‍♂️ 面试官:

思路很严谨,这些坑都是生产里实打实的,看得出来你是真用过。这一块我没其他问题了。

公众号“Rain的Java大神之路”
个人博客“www.javadashen.com”


原文地址: https://www.cveoy.top/t/topic/qHhH 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录