Redis 集群的安装配置与服务交互

主要围绕Redis高可用保障项目运行, 需要达到如下几点:

  • Redis 集群模式:使用 Redis Cluster(至少 3 主 3 从),某个主节点挂了,从节点自动提升为主,秒级切换
  • 哨兵模式:Redis Sentinel 监控节点状态,自动故障转移
  • 数据持久化:RDB + AOF 双持久化,Redis 重启后数据可恢复

一、 集群架构原理

Redis Cluster 采用无中心 P2P 架构 ,将数据空间划分为 16384 个哈希槽(Hash Slot) ,通过 CRC16(key) % 16384 计算每个 key 所属的槽位,再分配到不同主节点

  • 3 主 3 从:每个主节点负责一部分连续槽位,从节点同步主节点数据并在主节点故障时自动选举晋升
  • Gossip 协议:节点间通过集群总线(数据端口 +10000)进行心跳检测与状态同步,故障判定需半数以上主节点投票确认
  • 故障转移:主节点宕机后,其从节点发起选举,获得多数主节点投票后晋升为新主,整个过程约 10-20 秒

二、 安装与节点配置

1. 环境准备

以 3 主 3 从(6 节点)为例,假设部署在三台服务器上,每台运行 2 个实例(1 主 + 1 从):

节点 IP 端口 角色
node1 192.168.1.101 7000 主1
node1 192.168.1.101 7003 从1(主1的从)
node2 192.168.1.102 7001 主2
node2 192.168.1.102 7004 从2(主2的从)
node3 192.168.1.103 7002 主3
node3 192.168.1.103 7005 从3(主3的从)
2. 核心配置参数

每个节点创建独立的 redis.conf,关键配置如下:

bash 复制代码
# 节点端口(每个节点不同)
port 7000

# 开启集群模式(必须)
cluster-enabled yes

# 集群节点配置文件(Redis 自动维护,无需手动编辑)
cluster-config-file nodes-7000.conf

# 节点超时时间(毫秒),生产环境建议 5000-15000
cluster-node-timeout 15000

# 开启 AOF 持久化
appendonly yes

# 后台运行
daemonize yes

# 绑定 IP(生产环境建议绑定内网 IP)
bind 0.0.0.0

# 关闭保护模式(允许远程连接)
protected-mode no

# 访问密码(所有节点必须一致)
requirepass your_password

# 集群间通信密码(与 requirepass 保持一致)
masterauth your_password

Docker 部署额外配置 :必须显式声明对外通信地址,否则节点间无法互通

bash 复制代码
cluster-announce-ip 192.168.1.101
cluster-announce-port 7000
cluster-announce-bus-port 17000
3. 端口开放

每个节点需开放两个 TCP 端口

  • 数据端口:如 7000-7005(客户端连接)
  • 集群总线端口:数据端口 +10000,如 17000-17005(Gossip 通信)
bash 复制代码
# 防火墙放行示例
firewall-cmd --permanent --add-port=7000-7005/tcp
firewall-cmd --permanent --add-port=17000-17005/tcp
firewall-cmd --reload
4. 启动所有节点
bash 复制代码
redis-server /etc/redis/cluster/7000/redis.conf
redis-server /etc/redis/cluster/7001/redis.conf
redis-server /etc/redis/cluster/7002/redis.conf
redis-server /etc/redis/cluster/7003/redis.conf
redis-server /etc/redis/cluster/7004/redis.conf
redis-server /etc/redis/cluster/7005/redis.conf

三、 创建集群

Redis 5.0+ 使用 redis-cli --cluster create 命令,只需在任意一台机器执行一次

bash 复制代码
redis-cli --cluster create \
  192.168.1.101:7000 192.168.1.101:7003 \
  192.168.1.102:7001 192.168.1.102:7004 \
  192.168.1.103:7002 192.168.1.103:7005 \
  --cluster-replicas 1 \
  -a your_password
  • --cluster-replicas 1:每个主节点分配 1 个从节点
  • 执行后会提示槽位分配方案,输入 yes 确认

创建成功后会显示类似信息:

bash 复制代码
[OK] All 16384 slots covered.

四、 验证集群状态

bash 复制代码
# 连接集群(-c 启用集群模式,支持自动重定向)
redis-cli -c -h 192.168.1.101 -p 7000 -a your_password

# 查看集群整体状态
cluster info
# 关键指标:cluster_state:ok,cluster_slots_assigned:16384

# 查看节点拓扑(主从关系、槽位分布)
cluster nodes

# 健康检查
redis-cli --cluster check 192.168.1.101:7000 -a your_password

读写测试

bash 复制代码
# 在任意节点写入
set name redis
# 如果该 key 的槽不属于当前节点,会返回 MOVED 重定向

# 查看 key 所属槽位
cluster keyslot name

# 在从节点读取(验证主从同步)
get name

五、 客户端与服务交互流程

1. 客户端连接原理

客户端连接集群时,交互流程如下:

复制代码
客户端 ──① 连接任意节点(主/从均可)
         │
         ▼
节点 ──② 返回集群拓扑(各节点负责的槽位范围)
         │
         ▼
客户端 ──③ 计算 key 的哈希槽 CRC16(key) % 16384
         │
         ▼
   ┌─────┴─────┐
   │ 槽在本节点? │
   └─────┬─────┘
    YES  │  NO
     │    │
     ▼    ▼
  直接处理  返回 MOVED 重定向
           (客户端更新本地槽映射缓存并重试)
2. 编程语言客户端示例

以 Go 为例(使用 redis/go-redis):

Go 复制代码
import "github.com/redis/go-redis/v9"

rdb := redis.NewClusterClient(&redis.ClusterOptions{
    Addrs: []string{
        "192.168.1.101:7000",
        "192.168.1.101:7003",
        "192.168.1.102:7001",
        "192.168.1.102:7004",
        "192.168.1.103:7002",
        "192.168.1.103:7005",
    },
    Password: "your_password",
})

// 正常使用,客户端自动处理槽位路由
rdb.Set(ctx, "name", "redis", 0)
val, _ := rdb.Get(ctx, "name").Result()
3. 重定向机制
  • MOVED:槽位已永久迁移到另一节点,客户端更新本地缓存后重试
  • ASK :槽位正在迁移中(resharding),客户端需先发送 ASKING 命令再重试

六、 常见运维操作

扩容(添加新节点)
bash 复制代码
# 1. 添加新主节点
redis-cli --cluster add-node 192.168.1.104:7006 192.168.1.101:7000 -a your_password

# 2. 重新均衡槽位(reshard)
redis-cli --cluster reshard 192.168.1.101:7000 -a your_password

# 3. 添加新从节点(绑定到指定主节点)
redis-cli --cluster add-node 192.168.1.104:7007 192.168.1.101:7000 \
  --cluster-slave --cluster-master-id <master-node-id> -a your_password
缩容(删除节点)
bash 复制代码
# 1. 先迁移该主节点的所有槽位到其他节点
redis-cli --cluster reshard 192.168.1.101:7000 -a your_password

# 2. 删除空槽节点
redis-cli --cluster del-node 192.168.1.101:7000 <node-id> -a your_password
故障转移模拟
Go 复制代码
# 手动触发从节点晋升为主节点
redis-cli -h 192.168.1.101 -p 7003 -a your_password cluster failover

七、 关键注意事项

注意点 说明
多数据库不支持 集群模式下只能使用 db0,SELECT 命令不可用
批量操作限制 MSET/MGET 的 key 必须在同一哈希槽,可用 Hash Tags {user1000}.name 强制同槽
主从异步复制 存在短暂数据丢失风险,与哨兵模式相同
槽位必须全覆盖 默认 cluster-require-full-coverage yes,有槽未分配时集群拒绝写入
清理旧数据 创建集群前需确保节点为空,否则需删除 dump.rdbappendonly.aofnodes.conf
监控建议 监控 cluster_infocluster_nodes、节点内存/CPU/网络等指标

八、 整体流程总结

Go 复制代码
┌─────────────────────────────────────────────────────┐
│  1. 准备 6 个 Redis 实例(3主3从),各自独立配置      │
│  2. 启动所有节点                                      │
│  3. 执行 redis-cli --cluster create 创建集群          │
│  4. 验证 cluster_state:ok 且 16384 槽位全覆盖         │
│  5. 客户端使用 ClusterClient 连接,自动处理槽位路由    │
│  6. 主节点故障 → 从节点自动选举晋升 → 槽位重新映射     │
└─────────────────────────────────────────────────────┘

九、Docker Compose 部署

1. 目录结构
bash 复制代码
redis-cluster/
├── docker-compose.yml
├── redis.conf          # 配置模板
└── data/
    ├── node-7001/
    ├── node-7002/
    ├── node-7003/
    ├── node-7004/
    ├── node-7005/
    └── node-7006/

2. 配置文件
(1). redis.conf(配置模板)
bash 复制代码
# 端口(每个节点不同,启动时动态替换)
port ${PORT}

# 开启集群模式
cluster-enabled yes

# 集群元数据文件(Redis 自动维护)
cluster-config-file nodes.conf

# 节点超时时间(毫秒)
cluster-node-timeout 5000

# 开启 AOF 持久化
appendonly yes

# 关闭保护模式(集群环境必须)
protected-mode no

# 访问密码(所有节点必须一致)
requirepass your_password
masterauth your_password

# 声明对外地址(关键!容器部署必须配置)
cluster-announce-ip ${HOST_IP}
cluster-announce-port ${PORT}
cluster-announce-bus-port ${BUS_PORT}

cluster-announce-* 是容器部署的核心:Gossip 协议会交换节点 IP,如果不显式声明,节点上报的是容器内网 IP,外部客户端无法连接

(2). docker-compose.yml
bash 复制代码
version: '3.8'

services:
  redis-7001:
    image: redis:7.4-alpine
    container_name: redis-7001
    restart: always
    ports:
      - "7001:7001"
      - "17001:17001"
    volumes:
      - ./data/node-7001:/data
      - ./redis.conf:/usr/local/etc/redis/redis.conf
    command: >
      redis-server /usr/local/etc/redis/redis.conf
      --port 7001
      --cluster-announce-ip ${HOST_IP}
      --cluster-announce-port 7001
      --cluster-announce-bus-port 17001
    networks:
      - redis-cluster

  redis-7002:
    image: redis:7.4-alpine
    container_name: redis-7002
    restart: always
    ports:
      - "7002:7002"
      - "17002:17002"
    volumes:
      - ./data/node-7002:/data
      - ./redis.conf:/usr/local/etc/redis/redis.conf
    command: >
      redis-server /usr/local/etc/redis/redis.conf
      --port 7002
      --cluster-announce-ip ${HOST_IP}
      --cluster-announce-port 7002
      --cluster-announce-bus-port 17002
    networks:
      - redis-cluster

  redis-7003:
    image: redis:7.4-alpine
    container_name: redis-7003
    restart: always
    ports:
      - "7003:7003"
      - "17003:17003"
    volumes:
      - ./data/node-7003:/data
      - ./redis.conf:/usr/local/etc/redis/redis.conf
    command: >
      redis-server /usr/local/etc/redis/redis.conf
      --port 7003
      --cluster-announce-ip ${HOST_IP}
      --cluster-announce-port 7003
      --cluster-announce-bus-port 17003
    networks:
      - redis-cluster

  redis-7004:
    image: redis:7.4-alpine
    container_name: redis-7004
    restart: always
    ports:
      - "7004:7004"
      - "17004:17004"
    volumes:
      - ./data/node-7004:/data
      - ./redis.conf:/usr/local/etc/redis/redis.conf
    command: >
      redis-server /usr/local/etc/redis/redis.conf
      --port 7004
      --cluster-announce-ip ${HOST_IP}
      --cluster-announce-port 7004
      --cluster-announce-bus-port 17004
    networks:
      - redis-cluster

  redis-7005:
    image: redis:7.4-alpine
    container_name: redis-7005
    restart: always
    ports:
      - "7005:7005"
      - "17005:17005"
    volumes:
      - ./data/node-7005:/data
      - ./redis.conf:/usr/local/etc/redis/redis.conf
    command: >
      redis-server /usr/local/etc/redis/redis.conf
      --port 7005
      --cluster-announce-ip ${HOST_IP}
      --cluster-announce-port 7005
      --cluster-announce-bus-port 17005
    networks:
      - redis-cluster

  redis-7006:
    image: redis:7.4-alpine
    container_name: redis-7006
    restart: always
    ports:
      - "7006:7006"
      - "17006:17006"
    volumes:
      - ./data/node-7006:/data
      - ./redis.conf:/usr/local/etc/redis/redis.conf
    command: >
      redis-server /usr/local/etc/redis/redis.conf
      --port 7006
      --cluster-announce-ip ${HOST_IP}
      --cluster-announce-port 7006
      --cluster-announce-bus-port 17006
    networks:
      - redis-cluster

  # 初始化服务:所有节点启动后自动创建集群
  redis-init:
    image: redis:7.4-alpine
    container_name: redis-init
    depends_on:
      - redis-7001
      - redis-7002
      - redis-7003
      - redis-7004
      - redis-7005
      - redis-7006
    command: >
      sh -c "
      sleep 10 &&
      redis-cli -a your_password --cluster create
        redis-7001:7001
        redis-7002:7002
        redis-7003:7003
        redis-7004:7004
        redis-7005:7005
        redis-7006:7006
        --cluster-replicas 1
        --cluster-yes
      "
    networks:
      - redis-cluster

networks:
  redis-cluster:
    driver: bridge

redis-init 服务 :利用 depends_on 确保所有节点启动后再执行集群初始化,真正实现"一键部署"


3. 一键启动
(1). 创建目录
bash 复制代码
mkdir -p redis-cluster/data/node-{7001,7002,7003,7004,7005,7006}
cd redis-cluster
(2). 设置宿主机 IP 并启动
bash 复制代码
# 获取宿主机 IP(Linux)
export HOST_IP=$(hostname -I | awk '{print $1}')

# 或者手动指定
export HOST_IP=192.168.1.100

# 一键启动(8 个容器:6 节点 + 1 初始化)
docker compose up -d
(3). 查看启动日志
复制代码
# 查看初始化进度
docker logs -f redis-init

# 看到以下输出表示集群创建成功
# [OK] All 16384 slots covered.
4. 验证集群状态
bash 复制代码
# 连接任意节点查看集群信息
docker exec -it redis-7001 redis-cli -a your_password -c

# 查看集群状态
127.0.0.1:7001> CLUSTER INFO
# 期望输出:
# cluster_state:ok
# cluster_slots_assigned:16384
# cluster_slots_ok:16384
# cluster_known_nodes:6
# cluster_size:3

# 查看节点拓扑(主从关系、槽位分布)
127.0.0.1:7001> CLUSTER NODES

# 读写测试(-c 启用集群模式,自动处理 MOVED 重定向)
127.0.0.1:7001> SET user:1001 "Alice"
# -> Redirected to slot [5798] located at 172.x.x.x:7002
# OK

127.0.0.1:7002> GET user:1001
# "Alice"
5. 故障转移测试
bash 复制代码
# 1. 查看当前主节点
docker exec -it redis-7001 redis-cli -a your_password CLUSTER NODES | grep master

# 2. 杀掉一个主节点(如 redis-7002)
docker rm -f redis-7002

# 3. 等待 10 秒,从节点自动晋升
sleep 10

# 4. 查看集群状态,确认新主节点已接替
docker exec -it redis-7001 redis-cli -a your_password CLUSTER NODES | grep master

# 5. 数据仍然可读写
docker exec -it redis-7001 redis-cli -a your_password -c GET user:1001
6. 停止与清理
bash 复制代码
# 停止所有容器
docker compose down

# 彻底清理(包括数据卷)
docker compose down -v
rm -rf data/
7. 关键注意事项
注意点 说明
端口映射 每个节点需映射两个端口:数据端口(7001-7006)+ 总线端口(17001-17006)
cluster-announce- 容器部署必须显式声明宿主机 IP,否则外部客户端无法连接
密码一致性 requirepassmasterauth 必须所有节点一致
数据持久化 每个节点独立挂载 /data 目录,避免 AOF/RDB 互相覆盖
槽位全覆盖 初始化成功后 cluster_slots_assigned 必须等于 16384,否则集群不可用
客户端连接 应用代码需使用 Cluster 模式客户端(如 Go 的 redis.ClusterClient),并配置多个 seed 节点
9. Go 客户端连接示例
Go 复制代码
import "github.com/redis/go-redis/v9"

rdb := redis.NewClusterClient(&redis.ClusterOptions{
    Addrs: []string{
        "192.168.1.100:7001",
        "192.168.1.100:7002",
        "192.168.1.100:7003",
    },
    Password: "your_password",
})

// 正常使用,客户端自动处理槽位路由和重定向
rdb.Set(ctx, "user:1001", "Alice", 0)
val, _ := rdb.Get(ctx, "user:1001").Result()
相关推荐
Wang's Blog4 小时前
Java 接入Redis: Redis下载与源码编译安装
java·服务器·redis
焦虑的说说6 小时前
redis知识汇总
java·redis
Wang's Blog6 小时前
Java 接入Redis: 密码认证与远程连接配置
java·服务器·redis
Wang's Blog7 小时前
Java 接入Redis: Redis简介与NoSQL定位
java·服务器·redis
程序猿乐锅7 小时前
【黑马点评 | 第四篇】Redis缓存雪崩
java·数据库·spring boot·redis·缓存
Wang's Blog9 小时前
Java 接入Redis: 五大数据类型与存储结构选型
java·服务器·redis
Wang's Blog9 小时前
Java 接入Redis: 字符串与哈希类型操作命令
java·服务器·redis
Nano叶落1 天前
几十万个-key-扫不过来?-bigkeys、SCAN、RDB-离线分析三步定位最该清的-Big-Key
redis
shark-chili1 天前
关于AI辅助编程的认知
数据库·人工智能·redis·macos·缓存