Redis Cluster通过数据分片实现水平扩展,将16384个哈希槽分配到多个主节点,每个主节点配一个从节点提供高可用。与哨兵模式相比,Cluster方案通过数据分片突破单节点内存和性能上限,支持在线扩缩容。本文从集群拓扑设计、槽位分配、在线迁移、故障转移四个维度展开配置实战。
Redis Cluster集群拓扑与哈希槽分配
Redis Cluster采用无中心架构,每个节点通过Gossip协议交换集群状态。客户端连接任意节点,收到MOVED重定向后缓存槽位映射。16384个哈希槽的设计使扩缩容时迁移粒度可控,单个槽位迁移不影响其他槽位服务。
# 创建6节点集群(3主3从)
# redis.conf 配置
port 7000
cluster-enabled yes
cluster-config-file nodes-7000.conf
cluster-node-timeout 5000
cluster-announce-ip 192.168.1.100
cluster-announce-port 7000
cluster-announce-bus-port 17000
appendonly yes
appendfsync everysec
# 启动6个实例(7000-7005)
for port in 7000 7001 7002 7003 7004 7005; do
redis-server --port $port \
--cluster-enabled yes \
--cluster-config-file nodes-${port}.conf \
--cluster-node-timeout 5000 \
--appendonly yes &
done
# 创建集群,自动分配槽位
redis-cli --cluster create \
192.168.1.100:7000 192.168.1.100:7001 192.168.1.100:7002 \
192.168.1.100:7003 192.168.1.100:7004 192.168.1.100:7005 \
--cluster-replicas 1
# 查看集群状态
redis-cli -p 7000 cluster nodes
redis-cli -p 7000 cluster info
# cluster_state:ok
# cluster_slots_assigned:16384
# cluster_slots_ok:16384
# cluster_known_nodes:6
哈希槽映射与数据定向写入
Redis Cluster使用CRC16算法计算键的哈希槽:slot = CRC16(key) mod 16384。哈希标签{tag}内的字符串参与哈希计算,将相关键分配到同一槽位实现事务和Lua脚本的跨键操作。
# 哈希标签实现多键操作
# user:1001:profile 和 user:1001:orders 落在同一槽位
SET {user:1001}:profile "Alice"
SET {user:1001}:orders "order_list"
SET {user:1001}:cart "items"
# 同槽位键支持MULTI/EXEC事务
MULTI
GET {user:1001}:profile
GET {user:1001}:cart
EXEC
# 验证槽位分配
redis-cli -p 7000 cluster keyslot {user:1001}
# 返回: (integer) 12345
# 查看指定槽位所在节点
redis-cli -p 7000 cluster slots
# 1) 1) (integer) 0 -- 起始槽位
# 2) (integer) 5460 -- 结束槽位
# 3) 1) "192.168.1.100" -- 主节点IP
# 2) (integer) 7000
# 3) "abc123..." -- 节点ID
# 4) 1) "192.168.1.100" -- 从节点IP
# 2) (integer) 7003
# 客户端连接(Smart Client自动缓存路由表)
import redis
r = redis.RedisCluster(
startup_nodes=[
redis.ClusterNode('192.168.1.100', 7000),
redis.ClusterNode('192.168.1.100', 7001),
],
decode_responses=True,
cluster_error_retry_attempts=3,
read_from_replicas=True,
)
r.set('{user:1001}:name', 'Alice')
r.set('{user:1001}:age', '30')
# Pipeline批量操作(自动分扇出到各节点)
with r.pipeline() as pipe:
for i in range(1000):
pipe.set(f'key:{i}', f'value:{i}')
pipe.execute()
在线槽位迁移与集群扩容
Redis Cluster支持在线迁移槽位,迁移过程中不影响集群服务。MIGRATE命令将源节点数据原子转移到目标节点,迁移完成后更新集群路由表。
# 新增节点扩容
# 启动新主节点7006和新从节点7007
redis-server --port 7006 --cluster-enabled yes \
--cluster-config-file nodes-7006.conf --appendonly yes &
redis-server --port 7007 --cluster-enabled yes \
--cluster-config-file nodes-7007.conf --appendonly yes &
# 将新节点加入集群
redis-cli --cluster add-node 192.168.1.100:7006 192.168.1.100:7000
# 设置7007为7006的从节点
redis-cli --cluster add-node 192.168.1.100:7007 192.168.1.100:7000 \
--cluster-slave --cluster-master-id <7006node_id>
# 自动化分配(脚本方式)
redis-cli --cluster reshard 192.168.1.100:7000 \
--cluster-from ,, \
--cluster-to \
--cluster-slots 4096 \
--cluster-yes
# 查看迁移进度
redis-cli -p 7000 cluster nodes | grep migrating
redis-cli -p 7006 cluster nodes | grep importing
# 槽位迁移底层执行(手动方式)
# 1. 通知源节点准备迁移
CLUSTER SETSLOT 8192 MIGRATING
# 2. 通知目标节点准备导入
CLUSTER SETSLOT 8192 IMPORTING
# 3. 迁移数据
MIGRATE 192.168.1.100 7006 "" 0 5000 KEYS key1 key2 key3
# 4. 通知全集群槽位归属变更
CLUSTER SETSLOT 8192 NODE
故障检测与自动转移机制
当主节点故障,从节点通过Gossip协议检测到主节点下线后发起选举。获得多数主节点投票后升级为新主节点,接管故障主节点的槽位。
# 模拟主节点故障
redis-cli -p 7000 shutdown nosave
# 观察从节点7003选举升级
redis-cli -p 7001 cluster nodes
# 7000节点显示 disconnected/fail
# 7003节点从slave变为master
# 手动故障转移(计划内维护)
redis-cli -p 7003 cluster failover
# 命令序列:
# 1. 从节点通知主节点停止处理写入
# 2. 从节点同步主节点最新数据
# 3. 从节点广播升级消息
# 4. 主节点降级为从节点
# 强制故障转移(主节点不可达时)
redis-cli -p 7003 cluster failover FORCE
# 集群不一致修复
redis-cli --cluster fix 192.168.1.100:7000
# 查看集群健康状态
redis-cli --cluster check 192.168.1.100:7000
# [OK] All nodes agree about slots configuration.
# [OK] All 16384 slots covered.
# [OK] At least 1 replica for each master.
生产环境运维与监控指标
# Redis Exporter + Prometheus 监控
# 关键指标:
# redis_cluster_state 集群状态(1=ok)
# redis_cluster_slots_assigned 已分配槽位数
# redis_cluster_slots_ok 正常槽位数
# redis_cluster_known_nodes 已知节点数
# redis_cluster_links_peers 集群连接数
# redis_db_keys 键数量
# redis_memory_used_bytes 内存使用量
# redis_connected_clients 客户端连接数
# 超时与重试配置
import redis
r = redis.RedisCluster(
startup_nodes=[redis.ClusterNode('192.168.1.100', 7000)],
max_connections=100,
socket_timeout=2.0,
socket_connect_timeout=2.0,
retry_on_timeout=True,
cluster_error_retry_attempts=5,
read_from_replicas=True,
route_read_from_replicas_strategy="ALL_NODES",
)
# 批量SCAN集群所有键
def scan_cluster_keys(cluster, pattern='*'):
all_keys = []
for node in cluster.get_nodes():
if node['flags'] & redis.CLUSTER_NODE_MASTER:
client = redis.Redis(
host=node['host'], port=node['port'],
decode_responses=True
)
cursor = 0
while True:
cursor, keys = client.scan(
cursor=cursor, match=pattern, count=1000
)
all_keys.extend(keys)
if cursor == 0:
break
return all_keys
cluster-node-timeout设置过大导致故障检测延迟,过小导致网络抖动误判主节点下线。生产环境建议5-10秒,配合cluster-announce-ip确保跨网段Gossip通信正常。迁移槽位时控制单批迁移数量,避免大量MIGRATE阻塞源节点事件循环。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/rediscluster-ji-qun-jia-gou-yu-cao-wei-qian-yi-pei-zhi-shi/