Redis Cluster集群搭建配置与槽位分配故障转移实战

Redis Cluster通过数据分片实现水平扩展,将16384个哈希槽分配到多个节点上,每个节点负责一部分槽位的数据读写。与哨兵模式的读写分离不同,Cluster模式每个节点都能独立处理请求,通过客户端路由实现自动重定向。本文围绕6节点Cluster集群搭建、槽位分配、故障转移配置展开,提供生产环境部署方案。

Redis Cluster架构原理与数据分片机制

Redis Cluster采用哈希槽分片策略,使用CRC16算法对key计算哈希值后取模16384确定槽位:

# 哈希槽路由算法
SLOT = CRC16(key) mod 16384

# 示例:
# key "user:1001" -> CRC16 = 52341 -> 52341 % 16384 = 2989
# 槽位 2989 由对应节点处理

# 哈希标签(Hash Tag)确保相关key分配到同一槽位
# 使用{}包裹的部分参与哈希计算
# key: "user:{1001}:profile" 和 "user:{1001}:settings"
# 都使用 "1001" 计算槽位,保证同一用户数据在同一节点

Cluster架构组件:

Redis Cluster 6节点拓扑(3主3从):

  节点A (Master)  节点B (Master)  节点C (Master)
  槽位 0-5460     槽位 5461-10922 槽位 10923-16383
      |               |               |
  节点A' (Slave)  节点B' (Slave)  节点C' (Slave)

通信端口:
  6379  -> 客户端连接端口
  16379 -> 集群总线端口 (端口号 + 10000)
          用于节点间Ping/Pong、故障检测、配置更新

集群节点搭建与槽位手动分配

以6节点集群为例(3主3从),每个节点配置文件:

# 准备6个节点的配置文件
# node-7000.conf ~ node-7005.conf

# 通用配置模板 (以node-7000.conf为例)
cat > /etc/redis/node-7000.conf << 'EOF'
# 网络配置
port 7000
bind 192.168.1.10
protected-mode no
daemonize yes
pidfile /var/run/redis-7000.pid

# 日志配置
logfile /var/log/redis/redis-7000.log
loglevel notice

# 数据目录
dir /data/redis/7000

# 集群配置
cluster-enabled yes
cluster-config-file nodes-7000.conf
cluster-node-timeout 5000
cluster-announce-ip 192.168.1.10
cluster-announce-port 7000
cluster-announce-bus-port 17000

# 内存与持久化
maxmemory 4gb
maxmemory-policy allkeys-lru
appendonly yes
appendfsync everysec
EOF

# 批量生成其他5个节点配置
for port in 7001 7002 7003 7004 7005; do
    sed "s/7000/$port/g" /etc/redis/node-7000.conf > /etc/redis/node-$port.conf
    mkdir -p /data/redis/$port
done

# 启动所有节点
for port in 7000 7001 7002 7003 7004 7005; do
    redis-server /etc/redis/node-$port.conf
done

# 验证节点启动
redis-cli -p 7000 ping
# PONG
redis-cli -p 7000 cluster info
# cluster_enabled:1

使用redis-cli创建集群:

# 方式一:自动创建集群(推荐)
redis-cli --cluster create \
    192.168.1.10:7000 192.168.1.10:7001 192.168.1.10:7002 \
    192.168.1.10:7003 192.168.1.10:7004 192.168.1.10:7005 \
    --cluster-replicas 1
# --cluster-replicas 1 表示每个主节点配1个从节点

# 输出确认:
# >>> Performing hash slots allocation on 6 nodes...
# Master[0] -> Slots 0 - 5460
# Master[1] -> Slots 5461 - 10922
# Master[2] -> Slots 10923 - 16383
# Adding replica 192.168.1.10:7003 to 192.168.1.10:7000
# Adding replica 192.168.1.10:7004 to 192.168.1.10:7001
# Adding replica 192.168.1.10:7005 to 192.168.1.10:7002
# [OK] All 16384 slots covered

# 方式二:手动创建集群(精确控制)
# 1. 清空所有节点数据
for port in 7000 7001 7002 7003 7004 7005; do
    redis-cli -p $port flushall
    redis-cli -p $port cluster reset hard
done

# 2. 手动组建集群
# 将7001加入7000的集群
redis-cli -p 7001 cluster meet 192.168.1.10 7000
redis-cli -p 7002 cluster meet 192.168.1.10 7000
redis-cli -p 7003 cluster meet 192.168.1.10 7000
redis-cli -p 7004 cluster meet 192.168.1.10 7000
redis-cli -p 7005 cluster meet 192.168.1.10 7000

# 3. 手动分配槽位
redis-cli -p 7000 cluster addslots $(seq 0 5460)
redis-cli -p 7001 cluster addslots $(seq 5461 10922)
redis-cli -p 7002 cluster addslots $(seq 10923 16383)

# 4. 配置主从复制
# 获取各节点的Node ID
NODE_7000=$(redis-cli -p 7000 cluster myid)
NODE_7001=$(redis-cli -p 7001 cluster myid)
NODE_7002=$(redis-cli -p 7002 cluster myid)

# 将7003设为7000的从节点
redis-cli -p 7003 cluster replicate $NODE_7000
redis-cli -p 7004 cluster replicate $NODE_7001
redis-cli -p 7005 cluster replicate $NODE_7002

# 5. 验证集群状态
redis-cli -p 7000 cluster nodes
redis-cli -p 7000 cluster info

主从复制配置与自动故障转移

# 查看集群节点状态和主从关系
redis-cli -p 7000 cluster nodes
# 输出示例:
# 1a2b3c... 192.168.1.10:7000@17000 myself,master - 0 1700... 1 connected 0-5460
# 2b3c4d... 192.168.1.10:7001@17001 master - 0 1700... 2 connected 5461-10922
# 3c4d5e... 192.168.1.10:7002@17002 master - 0 1700... 3 connected 10923-16383
# 4d5e6f... 192.168.1.10:7003@17003 slave 1a2b3c... 0 1700... 4 connected
# 5e6f7a... 192.168.1.10:7004@17004 slave 2b3c4d... 0 1700... 5 connected
# 6f7a8b... 192.168.1.10:7005@17005 slave 3c4d5e... 0 1700... 6 connected

# 故障转移触发条件:
# cluster-node-timeout: 节点超过该时间(毫秒)无响应判定为故障
# 多数派: 至少半数主节点同意才能触发故障转移

# 模拟主节点故障
redis-cli -p 7000 shutdown nosave

# 等待cluster-node-timeout后观察从节点提升
sleep 6
redis-cli -p 7003 cluster nodes
# 7003节点应提升为master,接管7000的槽位

# 手动故障转移(计划性维护场景)
# 在从节点上执行CLUSTER FAILOVER
redis-cli -p 7003 cluster failover
# 选项:
#   无参数  - 正常故障转移(先同步数据再切换)
#   takeover - 强制接管(不等待同步,紧急场景)
#   force   - 跳过主节点确认

# 查看故障转移详情
redis-cli -p 7000 cluster info | grep cluster_state
redis-cli -p 7000 cluster info | grep cluster_slots_ok

客户端连接与Smart Client路由

使用redis-py(Python)和go-redis(Go)连接Cluster:

# Python redis-py Cluster连接
from redis.cluster import RedisCluster, ClusterNode

# 方式一:自动发现所有节点
rc = RedisCluster(
    host='192.168.1.10',
    port=7000,
    decode_responses=True,
    socket_timeout=5,
    socket_connect_timeout=5,
    retry_on_timeout=True,
    max_connections=100
)

# 方式二:指定启动节点列表
startup_nodes = [
    ClusterNode('192.168.1.10', 7000),
    ClusterNode('192.168.1.10', 7001),
    ClusterNode('192.168.1.10', 7002),
]
rc = RedisCluster(startup_nodes=startup_nodes, decode_responses=True)

# 基本操作(客户端自动路由到正确节点)
rc.set('user:1001', 'Alice')
rc.set('user:1002', 'Bob')
rc.set('{user:1001}:profile', 'admin')  # Hash Tag确保与user:1001同槽位

# 批量操作(需注意跨槽位问题)
# MSET跨槽位会报错,需使用pipeline
pipeline = rc.pipeline()
pipeline.set('key1', 'val1')
pipeline.set('key2', 'val2')
pipeline.set('key3', 'val3')
results = pipeline.execute()
# redis-py Cluster会自动将pipeline按节点分组执行

Go语言go-redis连接:

package main

import (
    "context"
    "fmt"
    "github.com/redis/go-redis/v9"
)

func main() {
    rdb := redis.NewClusterClient(&redis.ClusterOptions{
        Addrs: []string{
            "192.168.1.10:7000",
            "192.168.1.10:7001",
            "192.168.1.10:7002",
        },
        // 连接池配置
        PoolSize:     100,
        MinIdleConns: 10,
        // 超时配置
        DialTimeout:  5 * time.Second,
        ReadTimeout:  3 * time.Second,
        WriteTimeout: 3 * time.Second,
        // 路由配置
        RouteRandomly: true, // 读操作随机路由到从节点
        ReadOnly:      true,
        // 重试配置
        MaxRetries: 3,
    })
    defer rdb.Close()

    ctx := context.Background()

    // 基本操作
    err := rdb.Set(ctx, "user:1001", "Alice", 0).Err()
    if err != nil {
        panic(err)
    }

    val, err := rdb.Get(ctx, "user:1001").Result()
    fmt.Println("user:1001 =", val)

    // Hash Tag操作
    rdb.HSet(ctx, "{user:1001}:profile", "name", "Alice")
    rdb.HSet(ctx, "{user:1001}:settings", "role", "admin")
    // 两个key在同一槽位,可在同一pipeline中操作
}

集群扩容缩容与槽位迁移操作

# === 扩容:新增主从节点 ===
# 1. 启动新节点
redis-server /etc/redis/node-7006.conf  # 新主节点
redis-server /etc/redis/node-7007.conf  # 新从节点

# 2. 将新节点加入集群
redis-cli --cluster add-node 192.168.1.10:7006 192.168.1.10:7000
# 7006为新节点,7000为集群中已有节点

# 3. 将7007作为7006的从节点加入
redis-cli --cluster add-node 192.168.1.10:7007 192.168.1.10:7000 --cluster-slave --cluster-master-id $(redis-cli -p 7006 cluster myid)

# 4. 重新分配槽位(从现有节点迁移部分槽位到新节点)
redis-cli --cluster reshard 192.168.1.10:7000
# 交互式输入:
#   How many slots? 4096
#   Receiving node ID? <7006的Node ID>
#   Source node IDs? all(从所有现有主节点均衡分配)
#   或指定来源: <7000的ID> <7001的ID> <7002的ID>

# 5. 验证槽位分配
redis-cli -p 7000 cluster nodes
# 7006应获得约4096个槽位

# === 缩容:移除节点 ===
# 1. 将待移除节点的槽位迁移到其他节点
redis-cli --cluster reshard 192.168.1.10:7000 \
    --cluster-from <7006的Node ID> \
    --cluster-to <7000的Node ID> \
    --cluster-slots 4096 \
    --cluster-yes

# 2. 移除从节点
redis-cli --cluster del-node 192.168.1.10:7000 <7007的Node ID>

# 3. 移除主节点(槽位已全部迁出)
redis-cli --cluster del-node 192.168.1.10:7000 <7006的Node ID>

# 4. 验证集群状态
redis-cli -p 7000 cluster info
# cluster_slots_ok:16384
# cluster_state:ok

集群监控与常见故障处理

# 集群健康检查
redis-cli --cluster check 192.168.1.10:7000
# 输出:
# >>> Performing Cluster Check (using node 192.168.1.10:7000)
# M: 1a2b3c... 192.168.1.10:7000@17000
#    slots:[0-5460] (5461 slots) master
# M: 2b3c4d... 192.168.1.10:7001@17001
#    slots:[5461-10922] (5462 slots) master
# ...
# [OK] All nodes agree about slots configuration.
# [OK] All 16384 slots covered.

# 查看槽位分布
redis-cli -p 7000 cluster slots
# 返回JSON格式的槽位分布信息

# 集群性能测试
redis-cli --cluster call 192.168.1.10:7000 ping

# 监控集群关键指标
redis-cli -p 7000 cluster info | grep -E "cluster_state|cluster_slots|cluster_known_nodes"
# cluster_state:ok
# cluster_slots_assigned:16384
# cluster_slots_ok:16384
# cluster_known_nodes:6

# 常见故障处理

# 1. 槽位未完全覆盖
redis-cli -p 7000 cluster info | grep cluster_slots_ok
# 如果不是16384:
redis-cli --cluster fix 192.168.1.10:7000

# 2. 节点脱离集群
redis-cli -p 7003 cluster meet 192.168.1.10 7000
# 重新加入集群

# 3. 集群状态为fail(超过半数主节点宕机)
# 需要恢复足够的主节点才能恢复集群
# 紧急方案:使用cluster failover takeover强制提升从节点
redis-cli -p 7003 cluster failover takeover

# 4. 内存碎片整理(生产环境定期执行)
for port in 7000 7001 7002 7003 7004 7005; do
    redis-cli -p $port memory purge
    redis-cli -p $port info memory | grep used_memory_human
done

Redis Cluster在生产部署中需注意:跨槽位操作(MSET、MGET、MULTI/EXEC)不支持原生命令,需使用Hash Tag或客户端Pipeline按节点分组执行。cluster-node-timeout建议设置为网络RTT的5-10倍,过短会导致网络抖动误判故障转移。集群最少需要3主3从共6个节点才能保证可用性,单节点故障不会导致集群不可用。

原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/rediscluster-ji-qun-da-jian-pei-zhi-yu-cao-wei-fen-pei-gu/

(0)
小编小编
上一篇 16小时前
下一篇 16小时前

相关推荐