Java并发编程--33-Redis分布式缓存三大核心架构:主从、哨兵、分片,落地实战与选型

Redis分布式缓存三大核心架构:主从、哨兵、分片,落地实战与选型

作者 :Weisian
发布时间:2026年4月

直击痛点

"Redis用了好几年,主从、哨兵、分片傻傻分不清?面试官问'你们生产环境用的什么架构',你只能说'用了Redis集群',却说不清主从怎么配、哨兵怎么搭、分片怎么扩------架构选错,半夜OnCall。"

Redis作为分布式缓存的事实标准,其核心架构围绕三个目标展开:高可用、大容量、高并发。但很多开发者对这三种架构的认知是混乱的:

  • 主从架构:一主多从,读写分离,解决"读高并发",但主节点宕机需要人工介入;
  • 哨兵架构:在主从基础上增加自动故障转移,解决"高可用",但存储容量仍是单机瓶颈;
  • 分片集群:数据分片存储,解决"大容量",同时内置高可用,是大型系统的终极形态;
  • 面试高频问:"哨兵主观下线和客观下线的区别?""Redis Cluster的Hash Slot为什么是16384?""主从、哨兵、分片如何组合使用?"------答不上来=错失P7 offer。

本文将从Redis落地实践 的视角,结合底层原理代码实战避坑指南,彻底讲透Redis的三大核心架构:

✅ 架构一:主从架构------读写分离,夯实基础;

✅ 架构二:哨兵架构------自动故障转移,实现高可用;

✅ 架构三:分片集群------数据分片,突破容量瓶颈;

✅ 组合落地:主从+哨兵+分片的工业级部署方案;

✅ 避坑指南:复制风暴、脑裂、槽位迁移;

✅ 高频面试题标准答案(直接背);

✅ 生产环境选型决策树。

📌 核心一句话

主从架构解决"读高并发",哨兵架构解决"高可用",分片集群解决"大容量+高并发"。三者层层递进,从基础到完备,构成了Redis分布式缓存的完整落地体系。
📌 面试金句先记牢

  • 主从架构:一主多从,主写从读,异步复制,解决读压力,但主节点宕机需人工干预;
  • 哨兵架构:在主从基础上增加哨兵节点,监控主从健康状态,实现自动故障转移,解决高可用;
  • 分片集群:数据分片存储,每个分片内又有主从,内置故障转移,解决容量和写并发瓶颈;
  • 主观下线 :单个哨兵判断主节点不可达;客观下线:多数哨兵(quorum)确认主节点不可达,触发故障转移;
  • Raft选举:哨兵集群通过Raft算法选举Leader,由Leader执行故障转移;
  • Hash Slot:Redis Cluster将key空间划分为16384个槽位,每个key通过CRC16(key)%16384计算归属;
  • 生产环境终极架构:分片集群(主从+自动故障转移) = Redis Cluster,一站式解决高可用+大容量+高并发。

一、开篇:Redis三种架构的定位

Redis的架构演进,本质上是解决不同量级的问题:

架构 核心解决 配置复杂度 适用数据量 适用QPS
主从架构 读高可用、读性能 <10GB <8万读
哨兵架构 自动故障转移 <10GB <8万读
分片集群 容量+写性能 >100GB >10万

生活类比

  • 主从架构:图书馆主馆+分馆,主馆管入库,分馆管借阅。主馆着火,分馆可借阅但不能入库新书。

  • 哨兵架构:图书馆+自动监控系统,主馆着火自动切换到分馆当主馆,服务不中断。

  • 分片集群:按图书类别分馆,文学馆、科技馆、历史馆,每个馆又有主从+自动监控。

    ┌─────────────────────────────────────────────────────────────────┐
    │ Redis三种架构的演进关系 │
    ├─────────────────────────────────────────────────────────────────┤
    │ │
    │ 单机Redis ──→ 主从架构 ──→ 哨兵架构 ──→ 分片集群 │
    │ │ │ │ │ │
    │ ▼ ▼ ▼ ▼ │
    │ 基础读写 读扩展 高可用 容量+写扩展 │
    │ 有单点故障 主需人工介入 自动故障转移 终极方案 │
    │ │
    └─────────────────────────────────────────────────────────────────┘


二、架构一:Redis主从架构------读写分离的基础

2.1 核心痛点:为什么需要主从?

单机Redis:所有读写请求都打在一个节点,读多场景下CPU/内存直接打满,且一旦宕机数据全部丢失。

2.2 什么是主从架构?

主从架构是最基础的分布式Redis形态:一个主节点(Master)负责写操作,多个从节点(Slave/Replica)复制主节点数据,负责读操作

生活类比:图书馆主馆负责新书入库(写操作),多个分馆同步主馆藏书,读者只在分馆借书(读操作)。主馆着火,分馆不能入库新书,但还能借书。

2.3 主从复制原理

Redis主从复制采用异步复制,核心流程如下:

复制代码
┌─────────────────────────────────────────────────────────────────┐
│                    主从复制完整流程                              │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  从节点                              主节点                      │
│    │                                  │                         │
│    │──── 1. SLAVEOF 命令 ────────────→│                         │
│    │                                  │                         │
│    │←─── 2. +OK 响应 ─────────────────│                         │
│    │                                  │                         │
│    │──── 3. PSYNC {runid} {offset} ──→│                         │
│    │                                  │                         │
│    │        判断复制类型               │                         │
│    │      ├─ 首次连接 → 全量复制       │                         │
│    │      └─ 断线重连 → 增量复制       │                         │
│    │                                  │                         │
│    │←─── 4. +FULLRESYNC (全量复制) ───│                         │
│    │←─── 5. RDB文件传输 ──────────────│                         │
│    │                                  │                         │
│    │     加载RDB,清空自身数据          │                         │
│    │                                  │                         │
│    │←─── 6. 积压缓冲区命令 (增量复制) ─│                         │
│    │                                  │                         │
│    │     执行命令,追平数据             │                         │
│    │                                  │                         │
└─────────────────────────────────────────────────────────────────┘

核心原理:主从复制机制

  1. 全量复制:从节点第一次连接主节点,主节点生成RDB文件发送给从节点,从节点加载完成数据初始化;
  2. 增量复制 :后续主节点写入的数据,以repl_backlog缓冲区形式同步给从节点;
  3. 读写分离:业务代码中,写请求走主节点,读请求走从节点,分摊压力。

两种复制模式对比

模式 触发条件 传输内容 数据量 耗时
全量复制 首次连接、offset落后太多 RDB文件 + 积压缓冲区命令 秒~分钟级
增量复制 网络短暂断开后重连 积压缓冲区命令 毫秒级

2.4 实战配置:一主二从搭建

环境准备

3台Redis节点(端口:6379主,6380从,6381从)

主节点(6379)配置 redis-6379.conf
ini 复制代码
bind 0.0.0.0
port 6379
daemonize yes
logfile "6379.log"
dbfilename "dump-6379.rdb"
从节点(6380)配置 redis-6380.conf
ini 复制代码
bind 0.0.0.0
port 6380
daemonize yes
logfile "6380.log"
dbfilename "dump-6380.rdb"
# 声明主节点地址
replicaof 127.0.0.1 6379
# 从节点只读(默认开启)
replica-read-only yes
从节点(6381)配置 redis-6381.conf
ini 复制代码
bind 0.0.0.0
port 6381
daemonize yes
logfile "6381.log"
dbfilename "dump-6381.rdb"
replicaof 127.0.0.1 6379
replica-read-only yes
启动&验证
bash 复制代码
redis-server redis-6379.conf
redis-server redis-6380.conf
redis-server redis-6381.conf

# 连接主节点查看主从状态
redis-cli -p 6379 info replication

验证结果 :主节点显示2个从节点在线,主从复制状态为up

Java代码实现读写分离
java 复制代码
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Redis主从读写分离实现
 * 写请求 → 主节点(6379)
 * 读请求 → 轮询从节点(6380/6381)
 */
public class RedisMasterSlave {
    // 主节点连接池
    private static final JedisPool MASTER_POOL = new JedisPool("127.0.0.1", 6379);
    // 从节点列表
    private static final List<JedisPool> SLAVE_POOLS = Arrays.asList(
            new JedisPool("127.0.0.1", 6380),
            new JedisPool("127.0.0.1", 6381)
    );
    // 轮询计数器
    private static final AtomicInteger INDEX = new AtomicInteger(0);

    /**
     * 写数据:主节点
     */
    public static void set(String key, String value) {
        try (Jedis jedis = MASTER_POOL.getResource()) {
            jedis.set(key, value);
            System.out.println("主节点写入成功:" + key);
        }
    }

    /**
     * 读数据:轮询从节点
     */
    public static String get(String key) {
        // 轮询算法选择从节点
        int current = INDEX.getAndIncrement() % SLAVE_POOLS.size();
        JedisPool pool = SLAVE_POOLS.get(current);
        try (Jedis jedis = pool.getResource()) {
            String value = jedis.get(key);
            System.out.println("从节点" + jedis.getClient().getPort() + "读取成功:" + key);
            return value;
        }
    }

    public static void main(String[] args) {
        set("user:1", "张三");
        System.out.println(get("user:1"));
        System.out.println(get("user:1"));
    }
}

2.5 主从架构的优缺点

维度 评价 说明
读性能 ⭐⭐⭐⭐⭐ 多从节点分担读压力,线性扩展
写性能 ⭐⭐ 仍是单节点瓶颈
高可用 ⭐⭐ 主节点宕机需人工切换
数据安全 ⭐⭐⭐⭐ 多副本冗余
容量 ⭐⭐ 每节点存全量数据,受单机内存限制
一致性 ⭐⭐⭐ 异步复制,存在延迟

2.6 适用场景

  • 读多写少(读写比 > 10:1)
  • 数据量较小(<10GB)
  • 可接受主从延迟(毫秒~秒级)
  • 可接受人工故障转移

三、架构二:哨兵架构------自动化故障转移,高可用保障

3.1 为什么需要哨兵?

主从架构最大的痛点是:主节点宕机后,需要人工介入将从节点提升为主节点。哨兵(Sentinel)解决了这个问题------它是一个独立的进程,监控主从节点,实现自动故障转移。

生活类比:高速公路抢修队,平时巡逻监控(监控),发现事故(主节点下线),快速抢修(选新主),恢复通车(故障转移)。

3.2 哨兵核心原理

3.2.1 哨兵集群架构
复制代码
┌─────────────────────────────────────────────────────────────────┐
│                     哨兵集群架构                                 │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   ┌─────────────┐  ┌─────────────┐  ┌─────────────┐           │
│   │  哨兵节点1   │  │  哨兵节点2   │  │  哨兵节点3   │           │
│   │  (监控)     │  │  (监控)     │  │  (监控)     │           │
│   └──────┬──────┘  └──────┬──────┘  └──────┬──────┘           │
│          │                │                │                   │
│          └────────────────┼────────────────┘                   │
│                           │                                     │
│                    ┌──────▼──────┐                             │
│                    │  主节点      │                             │
│                    │  6379       │                             │
│                    └──────┬──────┘                             │
│                           │ 异步复制                            │
│          ┌────────────────┼────────────────┐                   │
│          │                │                │                   │
│     ┌────▼────┐      ┌────▼────┐      ┌────▼────┐             │
│     │ 从节点1  │      │ 从节点2  │      │ 从节点3  │             │
│     │ 6380    │      │ 6381    │      │ 6382    │             │
│     └─────────┘      └─────────┘      └─────────┘             │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

核心原理:哨兵三大核心能力

  1. 监控:定时发送心跳,检测主从节点状态;
  2. 通知:节点故障时,发送通知给管理员/业务系统;
  3. 自动故障转移:主节点下线,自动从从节点中选新主,其余节点指向新主。
3.2.2 主观下线 vs 客观下线
状态 含义 触发条件 后果
主观下线(SDOWN) 单个哨兵认为主节点不可达 哨兵向主节点发送PING,在down-after-milliseconds内无响应 仅该哨兵标记,不触发故障转移
客观下线(ODOWN) 多数哨兵认为主节点不可达 至少quorum个哨兵同意主观下线 触发故障转移
3.2.3 故障转移流程
复制代码
┌─────────────────────────────────────────────────────────────────┐
│                    哨兵故障转移流程                              │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  1. 主观下线                                                     │
│     └─ 哨兵A检测到主节点无响应 → 标记SDOWN                       │
│                                                                 │
│  2. 客观下线                                                     │
│     └─ 哨兵A询问其他哨兵 → 达到quorum → 标记ODOWN               │
│                                                                 │
│  3. 领导者选举                                                   │
│     └─ 哨兵集群通过Raft算法选举Leader                           │
│                                                                 │
│  4. 故障转移                                                     │
│     ├─ 从所有从节点中选最优从节点                                │
│     │   ├─ 优先级最高(replica-priority)                       │
│     │   ├─ 复制偏移量最大(数据最新)                           │
│     │   └─ 运行ID最小(兜底)                                   │
│     ├─ 执行 SLAVEOF NO ONE 提升为主                            │
│     ├─ 修改其他从节点指向新主                                   │
│     └─ 通知客户端更新配置                                       │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

3.3 实战配置:3节点哨兵集群

哨兵配置 sentinel-26379.conf
ini 复制代码
bind 0.0.0.0
port 26379
daemonize yes
logfile "26379.log"
# 监控主节点:mymaster是集群名,2表示客观下线票数
sentinel monitor mymaster 127.0.0.1 6379 2
# 心跳检测时间(毫秒)
sentinel down-after-milliseconds mymaster 3000
# 故障转移超时时间
sentinel failover-timeout mymaster 18000

另外两个哨兵配置(端口26380、26381),仅修改端口即可。

启动哨兵
bash 复制代码
redis-sentinel sentinel-26379.conf
redis-sentinel sentinel-26380.conf
redis-sentinel sentinel-26381.conf
故障模拟验证
  1. 杀死主节点:redis-cli -p 6379 shutdown
  2. 哨兵日志:检测主节点主观下线→客观下线→选举领导者→提升从节点为新主;
  3. 查看新主节点:redis-cli -p 26379 sentinel get-master-addr-by-name mymaster
Java代码使用哨兵
java 复制代码
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisSentinelPool;
import redis.clients.jedis.JedisPoolConfig;

import java.util.HashSet;
import java.util.Set;

/**
 * Redis哨兵架构客户端实现
 * 类比:高速公路抢修队 - 自动故障转移,客户端无感知
 */
public class SentinelDemo {
    
    private static JedisSentinelPool sentinelPool;
    
    static {
        // 哨兵节点列表
        Set<String> sentinels = new HashSet<>();
        sentinels.add("127.0.0.1:26379");
        sentinels.add("127.0.0.1:26380");
        sentinels.add("127.0.0.1:26381");
        
        // 连接池配置
        JedisPoolConfig poolConfig = new JedisPoolConfig();
        poolConfig.setMaxTotal(100);
        poolConfig.setMaxIdle(20);
        
        // 创建哨兵连接池(自动处理主从切换)
        // 参数:master名称、哨兵集合、连接池配置、连接超时
        sentinelPool = new JedisSentinelPool(
            "mymaster",      // 主节点名称(与sentinel.conf一致)
            sentinels,       // 哨兵节点列表
            poolConfig,      // 连接池配置
            2000             // 连接超时(ms)
        );
    }
    
    /**
     * 获取当前主节点连接
     */
    public static Jedis getMaster() {
        return sentinelPool.getResource();
    }
    
    /**
     * 写入数据
     */
    public static void write(String key, String value) {
        try (Jedis jedis = getMaster()) {
            jedis.set(key, value);
            System.out.println("[写操作] " + key + " = " + value);
        }
    }
    
    /**
     * 读取数据(哨兵模式下读写都走主节点)
     * 如需读写分离,需自行实现从节点获取逻辑
     */
    public static String read(String key) {
        try (Jedis jedis = getMaster()) {
            String value = jedis.get(key);
            System.out.println("[读操作] " + key + " = " + value);
            return value;
        }
    }
    
    public static void main(String[] args) throws InterruptedException {
        System.out.println("=== Redis哨兵架构演示 ===\n");
        
        // 1. 查看当前主节点
        String currentMaster = sentinelPool.getCurrentHostMaster().toString();
        System.out.println("当前主节点: " + currentMaster);
        
        // 2. 写入数据
        write("user:1001", "张三");
        write("user:1002", "李四");
        
        // 3. 读取数据
        read("user:1001");
        
        // 4. 模拟主节点故障(手动停止主节点)
        System.out.println("\n=== 模拟主节点故障 ===");
        System.out.println("请手动停止主节点Redis,哨兵将自动进行故障转移");
        System.out.println("故障转移后,客户端会自动切换到新主节点\n");
        
        // 等待故障转移
        for (int i = 0; i < 30; i++) {
            Thread.sleep(1000);
            try {
                String master = sentinelPool.getCurrentHostMaster().toString();
                if (!master.equals(currentMaster)) {
                    System.out.println("主节点已切换: " + master);
                    break;
                }
            } catch (Exception e) {
                System.out.println("等待故障转移...");
            }
        }
        
        // 5. 故障转移后继续写入
        write("user:1003", "王五");
        read("user:1001");
        read("user:1003");
        
        sentinelPool.close();
    }
}

3.4 哨兵架构的优缺点

维度 评价 说明
高可用 ⭐⭐⭐⭐⭐ 自动故障转移,无需人工干预
读性能 ⭐⭐⭐⭐ 可配置读写分离,但客户端需额外实现
写性能 ⭐⭐ 仍是单节点瓶颈
容量 ⭐⭐ 每节点存全量数据
运维复杂度 ⭐⭐⭐ 需额外部署哨兵节点
一致性 ⭐⭐⭐ 异步复制+故障转移可能丢数据

3.5 哨兵数量为什么是奇数?

核心原因 :哨兵集群通过Raft算法选举Leader,需要多数派(> N/2) 同意。

  • 3个哨兵:允许1个故障,2个正常即可选举
  • 4个哨兵:允许1个故障,但3个正常才能选举(3 > 4/2)
  • 4个哨兵比3个哨兵多一个节点,但容错能力相同(都是允许1个故障)

结论:奇数个哨兵可以在相同容错能力下节省资源。

3.6 适用场景

  • 对高可用要求高(SLA > 99.9%)
  • 数据量较小(<10GB)
  • 可接受主从延迟
  • 不想人工介入故障处理

四、架构三:分片集群------容量与并发的终极方案

4.1 为什么需要分片集群?

主从架构和哨兵架构都解决了高可用和读性能问题,但存储容量和写性能仍是单机瓶颈。当数据量超过单机内存(如100GB+),或写QPS超过单节点上限(如10万+),就需要分片集群。

生活类比:京东亚洲一号仓,全国多个仓库分片存储。上海仓存华东订单,北京仓存华北订单。单仓容量有限,多仓协同覆盖全国。

4.2 Redis Cluster核心原理

4.2.1 哈希槽(Hash Slot)

Redis Cluster将整个key空间划分为16384个哈希槽 ,每个key通过CRC16(key) % 16384计算归属槽位,每个主节点负责一部分槽位。

复制代码
┌─────────────────────────────────────────────────────────────────┐
│                    Redis Cluster 哈希槽分布                      │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   槽位: 0 ─────────────────────────────────────────────── 16383 │
│         │              │              │                        │
│         ▼              ▼              ▼                        │
│   ┌──────────┐   ┌──────────┐   ┌──────────┐                  │
│   │  节点A   │   │  节点B   │   │  节点C   │                  │
│   │ 主+从    │   │ 主+从    │   │ 主+从    │                  │
│   │ 槽0-5460 │   │槽5461-10922│ │槽10923-16383│               │
│   └──────────┘   └──────────┘   └──────────┘                  │
│                                                                 │
│   每个主节点都有从节点做高可用备份                               │
│   写入key时,根据槽位自动路由到对应节点                          │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

核心原理:哈希槽(Hash Slot)

  1. Redis Cluster默认有16384个哈希槽
  2. 集群启动时,哈希槽均匀分配给所有主节点;
  3. 数据写入时,计算key的CRC16值 % 16384,得到槽位,路由到对应节点;
  4. 每个主节点搭配从节点,保证高可用。
4.2.2 为什么是16384个槽?
原因 说明
网络开销 16384个槽位只需2KB(16384/8)就能描述,心跳包小
CRC16特性 CRC16算法产生0-65535,取16384(2^14)运算高效
集群规模 Redis官方建议集群不超过1000个节点,16384足够
4.2.3 MOVED与ASK转向
转向类型 含义 触发场景 客户端行为
MOVED 槽位永久迁移 槽位已完全迁移到另一个节点 更新本地路由表,重试
ASK 槽位临时迁移 槽位正在迁移过程中 先发ASKING,再重试

4.3 实战配置:3主3从分片集群

环境准备

6台Redis节点(端口:7001/7002/7003主,7004/7005/7006从)

节点配置 redis-7001.conf
ini 复制代码
bind 0.0.0.0
port 7001
daemonize yes
logfile "7001.log"
# 开启集群模式
cluster-enabled yes
# 集群配置文件
cluster-config-file nodes-7001.conf
cluster-node-timeout 5000

其余5个节点配置仅修改端口即可。

创建集群
bash 复制代码
redis-cli --cluster create 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 127.0.0.1:7006 --cluster-replicas 1
  • --cluster-replicas 1:1个主节点对应1个从节点;
  • 自动分配16384个哈希槽到3个主节点。
集群验证
bash 复制代码
redis-cli -p 7001 cluster info  # 查看集群状态
redis-cli -p 7001 cluster nodes # 查看节点列表
Java代码使用Redis Cluster
java 复制代码
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.JedisPoolConfig;

import java.util.HashSet;
import java.util.Set;

/**
 * Redis Cluster分片集群实战
 * 类比:分布式仓库 - 数据自动分片,客户端智能路由
 */
public class RedisClusterDemo {
    
    private static JedisCluster cluster;
    
    static {
        // 集群节点(只需提供部分种子节点)
        Set<HostAndPort> nodes = new HashSet<>();
        nodes.add(new HostAndPort("127.0.0.1", 7001));
        nodes.add(new HostAndPort("127.0.0.1", 7002));
        nodes.add(new HostAndPort("127.0.0.1", 7003));
        
        // 连接池配置
        JedisPoolConfig poolConfig = new JedisPoolConfig();
        poolConfig.setMaxTotal(200);
        poolConfig.setMaxIdle(50);
        poolConfig.setMinIdle(10);
        
        // 创建集群客户端(自动处理MOVED/ASK转向)
        cluster = new JedisCluster(nodes, poolConfig);
    }
    
    /**
     * 写入数据(自动路由)
     */
    public static void write(String key, String value) {
        cluster.set(key, value);
        System.out.println("[写操作] " + key + " = " + value);
    }
    
    /**
     * 读取数据
     */
    public static String read(String key) {
        String value = cluster.get(key);
        System.out.println("[读操作] " + key + " = " + value);
        return value;
    }
    
    /**
     * 查看key所在的槽位
     */
    public static void showSlot(String key) {
        // JedisCluster没有直接提供slot查询,通过连接任一节点执行
        // 此处简化演示
        int slot = getCRC16(key) & 16383;
        System.out.println("[槽位] " + key + " → 槽位 " + slot);
    }
    
    private static int getCRC16(String key) {
        int crc = 0xFFFF;
        for (char c : key.toCharArray()) {
            crc ^= (c & 0xFF);
            for (int j = 0; j < 8; j++) {
                if ((crc & 0x0001) != 0) {
                    crc = (crc >> 1) ^ 0x8408;
                } else {
                    crc = crc >> 1;
                }
            }
        }
        return crc;
    }
    
    public static void main(String[] args) {
        System.out.println("=== Redis Cluster 分片集群演示 ===\n");
        
        // 1. 写入数据(自动分布到不同节点)
        System.out.println("1. 写入1000条数据...");
        for (int i = 0; i < 1000; i++) {
            write("user:" + i, "user_value_" + i);
            write("order:" + i, "order_value_" + i);
        }
        
        // 2. 查看数据分布
        System.out.println("\n2. 数据槽位分布:");
        showSlot("user:100");
        showSlot("user:500");
        showSlot("user:900");
        
        // 3. 读取验证
        System.out.println("\n3. 读取验证:");
        read("user:100");
        read("user:500");
        read("user:900");
        
        // 4. Hash Tag示例(确保多key在同一槽位)
        System.out.println("\n4. Hash Tag示例:");
        cluster.set("user:{1001}", "张三");
        cluster.set("order:{1001}", "订单数据");
        // 两个key在同一个槽位,支持多key操作
        
        System.out.println("\n=== 集群信息 ===");
        System.out.println("节点数: 3主3从");
        System.out.println("槽位数: 16384");
        System.out.println("客户端自动处理MOVED/ASK转向");
        
        cluster.close();
    }
}

4.4 集群扩容与数据迁移

bash 复制代码
# 1. 添加新节点
redis-server /path/to/redis-cluster-7007.conf
redis-server /path/to/redis-cluster-7008.conf

# 2. 将新节点加入集群
redis-cli --cluster add-node 192.168.1.100:7007 192.168.1.101:7000
redis-cli --cluster add-node 192.168.1.100:7008 192.168.1.101:7000

# 3. 为新节点分配槽位(迁移数据)
redis-cli --cluster reshard 192.168.1.100:7001

# 4. 设置从节点
redis-cli -p 7008 cluster replicate <新主节点ID>

4.5 分片集群的优缺点

维度 评价 说明
容量 ⭐⭐⭐⭐⭐ 水平扩展,突破单机内存限制
写性能 ⭐⭐⭐⭐⭐ 多主节点并行写入
读性能 ⭐⭐⭐⭐⭐ 多主节点并行读取
高可用 ⭐⭐⭐⭐⭐ 内置主从+自动故障转移
运维复杂度 ⭐⭐⭐⭐ 需要管理集群、槽位、迁移
一致性 ⭐⭐⭐ 异步复制,主从切换可能丢数据

4.6 适用场景

  • 数据量大(>100GB)
  • 写QPS高(>5万)
  • 需要水平扩展
  • 有专业运维团队

五、组合落地:Redis终极架构

5.1 三者关系

复制代码
┌─────────────────────────────────────────────────────────────────┐
│                    Redis三种架构组合关系                          │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│   ┌─────────────────────────────────────────────────────────┐   │
│   │                  Redis Cluster                          │   │
│   │              (分片集群 = 主从 + 哨兵)                     │   │
│   │                                                         │   │
│   │   ┌─────────┐  ┌─────────┐  ┌─────────┐               │   │
│   │   │ 分片1   │  │ 分片2   │  │ 分片3   │               │   │
│   │   │ 主+从   │  │ 主+从   │  │ 主+从   │               │   │
│   │   │ 槽0-5460│  │5461-10922│ │10923-16383│             │   │
│   │   └─────────┘  └─────────┘  └─────────┘               │   │
│   │                                                         │   │
│   │   每个分片内部 = 主从架构(解决高可用)                   │   │
│   │   分片之间 = 哈希槽路由(解决容量)                       │   │
│   │   故障转移 = 内置哨兵机制(自动切换)                     │   │
│   └─────────────────────────────────────────────────────────┘   │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

5.2 生产环境部署建议

数据量 QPS 推荐架构 说明
<10GB <5万 主从 + 哨兵 简单够用,运维成本低
10-50GB 5-10万 Redis Cluster(3主3从) 初步分片
50-200GB 10-20万 Redis Cluster(5主5从) 中等规模
>200GB >20万 Redis Cluster(9主9从+) 大规模

5.3 三种架构对比总结

维度 主从架构 哨兵架构 分片集群
核心解决 读扩展 高可用 容量+写扩展
自动故障转移
读性能扩展 ✅(从节点) ✅(需额外配置) ✅(多主)
写性能扩展
容量扩展
运维复杂度
推荐场景 读多写少 高可用要求高 海量数据

六、避坑指南

6.1 复制风暴

问题:主节点挂掉重启后,多个从节点同时发起全量复制,导致主节点CPU/网络飙升。

解决

  • 使用树状复制:从节点挂载到从节点
  • 设置合理的复制积压缓冲区大小

6.2 哨兵脑裂

问题:网络分区导致多个哨兵各自为政,可能同时提升多个从节点为主。

解决

  • 配置奇数个哨兵(3/5/7)
  • 设置合理的quorum(如N/2+1)

6.3 集群扩容数据迁移

问题:扩容时槽位迁移会影响部分key的读写性能。

解决

  • 业务低峰期执行扩容
  • 分批次迁移,监控迁移进度

6.4 主从延迟导致数据丢失

问题:主节点宕机时,尚未同步到从节点的数据会丢失。

解决

  • 配置min-slaves-to-writemin-slaves-max-lag
  • 使用Redis Cluster,但仍有小概率丢失

七、高可用保障体系:缓存防护+多级缓存

7.1 哨兵模式:自动化故障转移(核心总结)

板块 内容
痛点 主从架构主节点宕机,服务无法写入
方案 Redis Sentinel(3节点奇数起步)
核心 主观下线(单哨兵判断)、客观下线(多数哨兵共识)、Raft选举、自动选主切换
代码 哨兵配置文件+故障模拟验证
局限 无法解决容量瓶颈,仅保证高可用
面试 哨兵数量必须奇数?避免脑裂;选举用Raft算法

7.2 多级缓存:最后一公里兜底

架构:L1本地缓存(Caffeine)+ L2分布式缓存(Redis)+ DB

  • 读请求:先查本地缓存→再查Redis→最后查DB;
  • 写请求:写DB→通过Canal订阅binlog→更新Redis→淘汰本地缓存;
  • 优势:降低网络延迟,Redis故障时本地缓存兜底。

7.3 缓存防护:三大杀手终极解法

问题 解决方案 核心原理
缓存穿透 布隆过滤器 过滤不存在的key,避免直接查DB
缓存雪崩 随机TTL+集群+熔断 避免key同时过期,集群分摊压力
缓存击穿 互斥锁+热点永不过期 防止并发请求击穿到DB

八、面试高频真题(标准答案直接背)

Q1:主从架构、哨兵架构、分片集群的区别?

答案

  1. 主从架构:一主多从,读写分离,解决读压力。主节点宕机需人工介入。
  2. 哨兵架构:在主从基础上增加哨兵节点,自动故障转移,解决高可用。
  3. 分片集群:数据分片存储,多主多从,内置故障转移,同时解决容量和写压力。

Q2:哨兵主观下线和客观下线的区别?

维度 主观下线(SDOWN) 客观下线(ODOWN)
判定者 单个哨兵 多数哨兵(quorum)
触发条件 PING无响应超过down-after-milliseconds 至少quorum个哨兵同意SDOWN
后果 仅标记,不触发故障转移 触发故障转移

Q3:为什么哨兵集群需要奇数个节点?

答案:哨兵通过Raft算法选举Leader,需要多数派(> N/2)同意。奇数个节点(如3、5)与比它多1的偶数节点(如4、6)容错能力相同,但节省资源。

Q4:Redis Cluster为什么是16384个哈希槽?

答案

  1. 网络开销:16384个槽位只需2KB(16384/8)描述,心跳包小
  2. CRC16特性:CRC16算法产生0-65535,取16384(2^14)运算高效
  3. 集群规模:Redis官方建议集群不超过1000个节点,16384足够

Q5:生产环境如何选择Redis架构?

答案

  • 数据量<10GB,读多写少 → 主从+哨兵
  • 数据量>100GB,写QPS高 → Redis Cluster
  • 对可用性要求极高 → 无论哪种架构都建议加哨兵或直接用Cluster

总结

1. 核心知识点速记口诀

复制代码
主从架构一拖N,主写从读压力分,
异步复制有延迟,主节点挂需人帮。

哨兵架构加监控,主观客观两下线,
奇数节点来选举,故障转移自动完。

分片集群哈希槽,16384槽均匀放,
每片内部主从备,容量并发一起扛。

三种架构层层进,选型要看数据量,
小用主从大用片,高可用上哨兵强。

2. 核心要点回顾

  1. 主从架构:读写分离,解决读压力,主需人工切换
  2. 哨兵架构:自动故障转移,解决高可用
  3. 分片集群:数据分片,解决容量和写压力
  4. 组合逻辑:分片集群内部已包含主从+哨兵,是终极方案

3. 生产环境落地建议

  • 中小规模(<10GB):主从+哨兵,简单稳定
  • 大规模(>100GB):Redis Cluster,一步到位
  • 核心业务:始终加哨兵或直接用Cluster
  • 永远记住:没有银弹,根据业务量级选型

写在最后

Redis架构的核心逻辑是层层递进、互补短板:主从解决读并发,哨兵解决高可用,分片解决大容量。

很多开发者在生产环境踩坑,本质是架构选型错误:用单机扛海量数据、用主从不做哨兵、用哨兵不分片。记住:没有最好的架构,只有最贴合业务的架构。

如果觉得有帮助,欢迎点赞、收藏、转发!

相关推荐
APIshop2 小时前
Python 爬虫获取京东商品详情 API 接口实战指南
java·服务器·数据库
wang09072 小时前
Linux性能优化之内存管理基础知识
java·linux·性能优化
范什么特西2 小时前
idea创建一个普通的Maven项目运行javaweb
java·maven·intellij-idea
好家伙VCC2 小时前
# 发散创新:用 Rust实现高性能物理引擎的底层架构设计与实战在游戏开发、虚拟仿真和机器人控
java·开发语言·python·rust·机器人
六道对穿肠2 小时前
Java 直连 USB 打印机实战:从 JNI 崩溃到「拷贝即用」的架构演进
java
user_admin_god3 小时前
OpenCode入门到入坑
java·人工智能·spring boot·语言模型
Maiko Star3 小时前
Claude Code安装教程
java·chatgpt·claude code
Aurorar0rua3 小时前
CS50 x 2024 Notes C - 04
java·开发语言
heimeiyingwang3 小时前
【无标题】
网络·缓存·docker·性能优化·架构