节点规划

最小可用 Redis Cluster 是 3 主 3 从。Redis Cluster 把 16384 个 slot 平均分给 3 个 master,每个 master 配 1 个从做高可用。

节点         IP              端口 (client / bus)     角色
redis01    172.21.0.2     6666 / 17001             master
redis02    172.21.0.3     6667 / 17002             master
redis03    172.21.0.4     6668 / 17003             master
redis04    172.21.0.5     6669 / 17004             slave  → redis01
redis05    172.21.0.6     6670 / 17005             slave  → redis02
redis06    172.21.0.7     6671 / 17006             slave  → redis03

端口分两类:

  • client port(如 6379):给应用连接
  • cluster bus port(默认 +10000,如 16379):节点间 gossip 通信,必须开放

compose 编写

所有 6 个节点共用一份 redis.conf(内容相同,运行时由启动命令加 --port 区分)。先建 redis.conf:

# 集群模式
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000

# 节点对外宣告(容器网络下必须显式指定)
cluster-announce-ip 172.21.0.2       # 每个节点不同
cluster-announce-port 6379           # 每个节点不同
cluster-announce-bus-port 16379      # 每个节点不同

# 持久化
appendonly yes
appendfsync everysec

# 安全(生产环境必开)
requirepass <REPLACE_WITH_STRONG_PASSWORD>
masterauth  <REPLACE_WITH_STRONG_PASSWORD>
密码与 IP 必须替换。原 Memo 中的内网 IP(172.21.0.0/16)与硬编码弱密码(1qaz2wsx)只能用于本地学习;生产环境必须改成强密码与实际可路由的 IP。

docker-compose.yml:

version: "3.8"

services:
  redis01:
    image: redis:7.2-alpine
    container_name: redis01
    command: redis-server /usr/local/etc/redis/redis.conf
    volumes:
      - ./conf/redis01.conf:/usr/local/etc/redis/redis.conf
      - redis01-data:/data
    ports:
      - "6666:6379"
      - "17001:16379"
    networks:
      cluster_net:
        ipv4_address: 172.21.0.2

  redis02:
    image: redis:7.2-alpine
    container_name: redis02
    command: redis-server /usr/local/etc/redis/redis.conf
    volumes:
      - ./conf/redis02.conf:/usr/local/etc/redis/redis.conf
      - redis02-data:/data
    ports: [ "6667:6379", "17002:16379" ]
    networks: { cluster_net: { ipv4_address: 172.21.0.3 } }

  redis03:
    image: redis:7.2-alpine
    container_name: redis03
    command: redis-server /usr/local/etc/redis/redis.conf
    volumes: [ "./conf/redis03.conf:/usr/local/etc/redis/redis.conf", "redis03-data:/data" ]
    ports: [ "6668:6379", "17003:16379" ]
    networks: { cluster_net: { ipv4_address: 172.21.0.4 } }

  redis04:
    image: redis:7.2-alpine
    container_name: redis04
    command: redis-server /usr/local/etc/redis/redis.conf
    volumes: [ "./conf/redis04.conf:/usr/local/etc/redis/redis.conf", "redis04-data:/data" ]
    ports: [ "6669:6379", "17004:16379" ]
    networks: { cluster_net: { ipv4_address: 172.21.0.5 } }

  redis05:
    image: redis:7.2-alpine
    container_name: redis05
    command: redis-server /usr/local/etc/redis/redis.conf
    volumes: [ "./conf/redis05.conf:/usr/local/etc/redis/redis.conf", "redis05-data:/data" ]
    ports: [ "6670:6379", "17005:16379" ]
    networks: { cluster_net: { ipv4_address: 172.21.0.6 } }

  redis06:
    image: redis:7.2-alpine
    container_name: redis06
    command: redis-server /usr/local/etc/redis/redis.conf
    volumes: [ "./conf/redis06.conf:/usr/local/etc/redis/redis.conf", "redis06-data:/data" ]
    ports: [ "6671:6379", "17006:16379" ]
    networks: { cluster_net: { ipv4_address: 172.21.0.7 } }

networks:
  cluster_net:
    driver: bridge
    ipam:
      config:
        - subnet: 172.21.0.0/16

volumes:
  redis01-data: {}
  redis02-data: {}
  redis03-data: {}
  redis04-data: {}
  redis05-data: {}
  redis06-data: {}

初始化集群

启动所有节点:

docker compose up -d

进入任一节点,把 6 个节点组成 cluster:

docker exec -it redis01 bash

# Redis 7 用 redis-cli 直接 create,--cluster-replicas 1 表示每个 master 1 个 slave
redis-cli --cluster create \
  172.21.0.2:6379 172.21.0.3:6379 172.21.0.4:6379 \
  172.21.0.5:6379 172.21.0.6:6379 172.21.0.7:6379 \
  --cluster-replicas 1 \
  -a <REPLACE_WITH_STRONG_PASSWORD>

看到 Can I set the above configuration? (type 'yes' to accept) 时输入 yes。最后一行应类似 [OK] All 16384 slots covered。

若忘了密码或 IP 写错,删掉 redis*-data 卷重来:docker compose down -v。

验证

docker exec -it redis01 redis-cli -a <PASSWORD> cluster info
# cluster_state:ok
# cluster_slots_assigned:16384
# cluster_known_nodes:6
# cluster_size:3

docker exec -it redis01 redis-cli -a <PASSWORD> cluster nodes

每个 master 应带一个 slave 标记的节点。写入测试:

redis-cli -c -h 172.21.0.2 -a <PASSWORD> set hello world
# -> Redirected to slot [866] located at 172.21.0.3:6379
# OK
redis-cli -c -h 172.21.0.2 -a <PASSWORD> get hello
# "world"

-c 表示 cluster 模式:客户端会自动按 slot 跳转到对应 master。

客户端接入

应用端用任意 Redis 客户端时,任意节点 IP 都能连,客户端库会自动跳转 slot:

// Java (Lettuce)
RedisClusterClient.create(
    ClientResources.create(),
    RedisURI.create("redis://:<PASSWORD>@172.21.0.2:6379"),
    RedisURI.create("redis://:<PASSWORD>@172.21.0.3:6379"),
    RedisURI.create("redis://:<PASSWORD>@172.21.0.4:6379"),
    RedisURI.create("redis://:<PASSWORD>@172.21.0.5:6379"),
    RedisURI.create("redis://:<PASSWORD>@172.21.0.6:6379"),
    RedisURI.create("redis://:<PASSWORD>@172.21.0.7:6379")
);
# Python (redis-py)
from redis.cluster import RedisCluster
rc = RedisCluster(
    host="172.21.0.2", port=6379,
    password="<PASSWORD>",
    startup_nodes=[
        {"host": "172.21.0.2", "port": 6379},
        {"host": "172.21.0.3", "port": 6379},
        {"host": "172.21.0.4", "port": 6379},
    ],
)
rc.set("foo", "bar")
客户端 不要写死节点 IP。建议用 DNS / 服务发现,或者把全部 6 个节点都列在 startup_nodes,客户端会自动选最优。

故障转移演示

手动让 redis01(master)下线:

docker stop redis01

等 5–10 秒后查看集群状态:

docker exec redis02 redis-cli -a <PASSWORD> cluster nodes

原本 redis01 的 slave(redis04)会被晋升为新 master,状态类似 master。把 redis01 启动回来,它会以 slave 身份加入集群:

docker start redis01

日常运维

  • 扩缩容:redis-cli --cluster add-node / --cluster reshard。
  • 备份:BGSAVE 落到 AOF/RDB,跨节点复制到异地。
  • 慢查询:SLOWLOG GET 10。
  • 内存水位:INFO memory 看 used_memory_human,超过 70% 警惕。

使用准则

  1. 至少 3 主 3 从,单 master 集群无法高可用。
  2. 不要在 cluster 模式下用 SELECT、KEYS *、MULTI 跨 slot 事务——会被路由打散。
  3. 大批量写入(rehash / migration)走 --cluster reshard,不要靠客户端 hash tag 强制。
  4. 数据迁移前先在 redis 6+ 用 RIOT / redis 7 自带的 MIGRATE ... AUTH。
参考资料