介绍一下Redis的高可用方案
介绍一下Redis的高可用方案
面试官您好,Redis 的高可用方案我主要从四个递进层面来介绍,覆盖从基础到企业级生产环境的所有核心方案,每个方案我都会讲清楚它的核心原理、优缺点和适用场景。
主从复制(基础高可用)✅
这是所有高可用方案的基石,核心解决数据备份和读写分离问题。
核心架构
工作原理
- 从节点启动后,向主节点发送
PSYNC命令 - 首次连接:主节点执行全量复制(生成 RDB 文件发送给从节点)
- 后续连接:主节点执行增量复制(只发送偏移量之后的命令)
优缺点
- ✅ 优点:实现简单,读能力线性提升,数据有备份
- ❌ 缺点:不能自动故障转移,主节点挂了需要手动切换;单主写能力瓶颈
适用场景
- 测试环境、小流量业务(QPS < 1000)
- 只需要读写分离和数据备份的场景
哨兵模式(Sentinel)🛡️
在主从复制基础上,解决自动故障转移问题,是中小流量生产环境最常用的方案。
三大核心功能
- 监控:持续检测主从节点是否正常运行
- 选主:主节点故障时,自动从从节点中选举新主
- 通知:将故障转移结果通知客户端和其他节点
核心架构
故障转移流程
- 单个哨兵发现主节点不可达 → 主观下线
- 超过半数哨兵确认主节点不可达 → 客观下线
- 哨兵集群选举出一个领导者哨兵
- 领导者哨兵从从节点中选举新主节点
- 更新配置,通知客户端,旧主节点变为从节点
优缺点
- ✅ 优点:自动故障转移,高可用,运维简单
- ❌ 缺点:不能水平扩展,单主写能力瓶颈;哨兵集群本身也需要高可用
适用场景
- 中小流量生产环境(QPS < 10000)
- 写压力不大,读压力较大的业务
Redis Cluster 集群模式(分片高可用)⚡
解决单节点写能力瓶颈和水平扩展问题,是大流量生产环境的标准方案。
核心概念:16384 个哈希槽
- Redis Cluster 将所有数据映射到 16384 个哈希槽中
- 每个主节点负责一部分哈希槽
- 客户端通过
CRC16(key) % 16384计算 key 所在的槽,直接连接对应节点
核心架构
故障转移机制
- 每个节点都监控其他节点的状态
- 当某个主节点故障时,它的从节点会自动晋升为新主节点
- 集群自动更新哈希槽映射,客户端通过MOVED重定向到新节点
优缺点
- ✅ 优点:线性水平扩展,写能力和读能力都能线性提升;自动故障转移
- ❌ 缺点:运维复杂;不支持跨槽的多键操作(如MGET、MSET);数据迁移有短暂影响
适用场景
- 大流量高并发生产环境(QPS > 10000)
- 数据量超过单节点内存容量的业务
三大核心方案对比表 📊
| 方案 | 最小节点数 | 故障转移 | 水平扩展 | 写能力 | 读能力 | 运维复杂度 | 适用场景 |
|---|---|---|---|---|---|---|---|
| 主从复制 | 2 | ❌ 手动 | ❌ 不支持 | 单主瓶颈 | 线性提升 | 低 | 测试环境、小流量 |
| 哨兵模式 | 5(3 哨兵 + 2 主从) | ✅ 自动 | ❌ 不支持 | 单主瓶颈 | 线性提升 | 中 | 中小流量生产 |
| Redis Cluster | 6(3 主 3 从) | ✅ 自动 | ✅ 线性扩展 | 线性提升 | 线性提升 | 高 | 大流量高并发 |
企业级进阶优化与云原生方案 💡
- 读写分离优化:使用客户端分片或代理(如 Twemproxy、Codis)实现更灵活的读写分离
- 持久化策略:主节点关闭 RDB,从节点开启 RDB+AOF 混合持久化,平衡性能和数据安全
- 慢查询监控:定期清理慢查询日志,设置合理的超时时间
- 云原生方案:在 K8s 环境中使用 Redis Operator(如 Redis Cluster Operator),实现自动化部署、扩缩容和故障转移
- 云厂商托管:对于核心业务,建议使用阿里云 Redis、腾讯云 Redis 等托管服务,省心又稳定
面试加分总结 ✨
在实际项目中,我一般会根据业务阶段和流量规模来选择合适的方案:
- 项目初期:主从复制 + 哨兵,运维简单,成本低
- 业务增长期:当单主写能力达到瓶颈时,迁移到 Redis Cluster
- 云原生环境:优先使用 Redis Operator,自动化运维效率更高
- 核心业务:使用云厂商托管 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<String> 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<String, Object> redisTemplate(LettuceConnectionFactory connectionFactory) {
RedisTemplate<String, Object> 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 技术亮点:主从切换时的客户端重试机制
技术难点与解决方案汇总 🛠️
| 技术难点 | 问题描述 | 根本原因 | 解决方案 |
|---|---|---|---|
| 主从复制数据不一致 | 从节点数据比主节点旧,读取到脏数据 | 主从同步有延迟;网络抖动导致增量复制中断 | 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 命令把一台从库提成主库。这个过程运维介入慢,业务中断时间长。所以主从复制只是地基,高可用得在这个基础上再加东西。
🙋♂️ 面试官:
明白,那你们怎么解决自动切换的问题?
🛡️ 哨兵 Sentinel —— 自动容灾
👨💻 候选人:
我们在主从之上部署哨兵集群。哨兵本质上也是一个 Redis 进程,只是它不存数据,专门盯着主节点和从节点的健康状态。
整个机制我分几步说:
- 监控:每个哨兵定期 PING 主从节点。
- 主观下线:如果一个哨兵发现主节点 PING 不通了,会认为它“主观下线”。
- 客观下线:为了避免网络抖动误判,必须达到配置的
quorum数量(比如3个哨兵里至少2个)都认为它挂了,才会真正判定“客观下线”,触发故障转移。 - 选举 Leader:哨兵集群内部用类似 Raft 的机制选出一个哨兵领导者来执行切换。
- 切换:Leader 从健康的从库中选一个提升为新主,改配置,然后通知其他从库去复制新主。最关键的是,它还会通过 Pub/Sub 机制通知客户端新主地址。
这样,整个切换过程几秒到十几秒,运维完全不用干预。我们很多中小型项目,用哨兵方案就足够了。😊
🙋♂️ 面试官:
不错,那你遇到过脑裂的问题吗?怎么解决的?
👨💻 候选人:
遇到过,这是个经典坑。比如网络分区,旧主其实没死,只是和哨兵失联了,哨兵选出了新主,这时就存在两个 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 协议互相交换状态,没有中心代理,每个节点都知道整个集群的拓扑。
- 内建故障转移:每个主节点可以挂至少一个从节点。当某个主挂了,它的从节点会发起选举(由集群中其他主节点投票),晋升为新主,这个过程不依赖任何外部哨兵,集群自己搞定。
🙋♂️ 面试官:
那客户端怎么知道去哪个节点找数据?
👨💻 候选人:
这就要用智能客户端,像 Java 里的 Lettuce 或 JedisCluster。它们会缓存槽与节点的映射关系,计算好槽后直接连目标节点。如果因为扩缩容槽迁移了,节点会返回 MOVED 错误码和新节点地址,客户端会自动更新映射并重试,对业务几乎透明。👍
🙋♂️ 面试官:
Cluster 有什么限制?
👨💻 候选人:
最主要的是多 Key 操作限制,比如 mget、事务,要求所有 Key 必须在同一个槽。我们可以用 {} 强制让部分 Key 落到同一槽,但不能滥用。另外跨节点的事务不支持,所以我们会在设计 Key 时尽量规避这种需求。
📊 对比与选型
🙋♂️ 面试官:
最后你帮我总结一下,什么时候选什么方案?
👨💻 候选人:
我画个表吧,一目了然:
| 方案 | 自动故障转移 | 写扩展 | 运维复杂度 | 适合场景 |
|---|---|---|---|---|
| 主从 | ❌ 手动 | ❌ | ⭐ | 测试、对可用性无要求 |
| 哨兵 Sentinel | ✅ 自动 | ❌ (只有读扩展) | ⭐⭐ | 中小型项目首选,数据量不大但必须自动容灾 |
| Cluster | ✅ 自动 | ✅ (读写都可扩展) | ⭐⭐⭐ | 大流量/海量数据,互联网核心链路 |
我们一般的路径是:项目初期用哨兵,等数据量或写入压力上来了,再平滑迁移到 Cluster。迁移前会在备集群先建好 Cluster,用双写或同步工具切流。
☕ Java 研发配合点
🙋♂️ 面试官:
很好,最后说下你们 Java 客户端怎么配合这些方案?
👨💻 候选人:
- 哨兵模式:我们用 Lettuce 的
RedisSentinelClient或 JedisSentinelPool,只需配置哨兵地址,框架自动发现并连接主库,主从切换时客户端能收到通知并重连。 - Cluster 模式:直接用 Spring Boot 默认的 Lettuce,开启
spring.redis.cluster.nodes配置,它会自动处理MOVED、ASK重定向,底层长连接会跟 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<String, Object> redisTemplate(
LettuceConnectionFactory factory) {
RedisTemplate<String, Object> 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<String, Object> redisTemplate;
public Optional<User> 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-write 和 min-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 主节点必须配上这些反脑裂参数,否则上线审批不通过。🚫
🙋♂️ 面试官:
思路很严谨,这些坑都是生产里实打实的,看得出来你是真用过。这一块我没其他问题了。
