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/