认识
一、认识
Redis Cluster
是一套功能强大的分布式存储方案,能够在高并发、大数据量的场景下提供良好的性能和高可用性。其核心在于利用 16384
个哈希槽将数据分散到各个节点,并通过内建的故障检测、自动故障转移及 Gossip
协议实现集群状态维护。虽然 Redis Cluster
在多键操作和数据一致性方面存在一定局限,但在适当的应用场景下(例如缓存、会话存储、实时数据处理等),它依然是一个高效且可靠的解决方案。通过合理的规划、配置和运维,可以充分发挥 Redis Cluster
的优势,构建出一个既可扩展又高可用的分布式缓存或存储系统。
Redis Cluster
架构:
graph LR
%% 定义集群中的三个主节点及其负责的哈希槽范围
M1[Master 1<br/>(槽 0 - 5460)]
M2[Master 2<br/>(槽 5461 - 10922)]
M3[Master 3<br/>(槽 10923 - 16383)]
%% 定义对应的从节点
S1[Slave 1<br/>(复制 Master 1)]
S2[Slave 2<br/>(复制 Master 2)]
S3[Slave 3<br/>(复制 Master 3)]
%% 主从复制关系
M1 --- S1
M2 --- S2
M3 --- S3
%% 节点之间无中心分布,客户端根据哈希槽映射进行重定向
subgraph Redis Cluster
M1
M2
M3
S1
S2
S3
end
-
主从复制: 每个主节点都有至少一个从节点作为备份,当主节点出现故障时,对应的从节点可以被提升为新的主节点,从而实现自动故障转移,确保高可用性。
-
主节点与哈希槽分布:
Redis Cluster
将整个键空间固定分为16384
个哈希槽。图中三个主节点分别负责一部分槽范围(例如Master 1
负责槽0-5460
),这样保证数据均匀分布。 -
无中心设计与客户端重定向: 集群中的每个节点都平等工作。客户端在初次连接时会通过任意节点获取整个槽的映射信息,然后根据
key
所在的槽决定连接哪个主节点。如果请求发送到错误的节点,节点会返回MOVED
重定向提示客户端连接正确的节点。
Redis Cluster
核心特性:
-
自动分片: 分布式架构,
Redis Cluster
采用了一种简单而高效的 哈希槽 机制来实现数据分片与分布:Redis Cluster
将整个键空间划分为16384
个哈希槽。每个键在经过CRC16
哈希算法后,通过对16384
取模计算出该键所属的槽编号。集群中的每个Master
节点会被分配若干个连续或非连续的哈希槽。当客户端发起操作时,首先计算键对应的哈希槽编号,然后直接路由到负责该槽的节点。理论上,采用CRC16
后取模可以将键均匀分布到16384
个槽中,进而实现各节点之间的数据均衡。若发现数据或请求分布不均,可以通过手动或自动化工具(如redis-cli
的--cluster rebalance
命令)对槽进行迁移,达到负载均衡目的。当集群扩容或缩容时,Redis Cluster
能够自动或手动将部分槽从一个节点迁移到另一个节点,保障系统在负载变化时保持稳定和高效。-
算法: 在分布式系统中,为了将数据均匀地分布到多个节点上,通常需要采用合适的分区方案。下面详细介绍三种常见的分区方案: 节点取余、一致性哈希分区 以及 虚拟槽分区。
-
节点取余分区(
Modulo
分区): 对每个数据的key
计算哈希值,然后对节点数取模,结果即为该key
对应的节点编号。节点取余 算法直观,编码和理解都比较简单。但是, 当节点数量发生变化时(如扩容或缩容),大部分key
的映射关系都会改变,导致大量数据需要重新分配。如果哈希函数分布不够均匀,可能会出现部分节点数据量较大,形成热点问题。在节点取余的方案下, 可以考虑成倍的扩容、缩容。比如, 之前有三台机器, 成倍扩容到六台、九台等, 可以减小数据偏移的概率。节点取余分区, 简单易实现,但扩容和缩容时需要大规模重新映射,适用于节点数稳定且数据量较小的场景。nodeIndex = hash(key) mod N
其中
N
表示当前的节点总数。 -
一致性哈希分区: 构建哈希环, 将整个哈希空间看作一个 环, 每个节点也通过哈希函数映射到环上的某个位置。将
key
也映射到同一环上,然后按照顺时针方向找到的第一个节点就是该key
的存储节点。当增加或移除一个节点时,只会影响环上该节点附近的部分key
,不需要全局重新分布。一致性哈希分区 节点加入或退出时,只需重新映射受影响区域的key
,从而大大减少数据迁移量。对于部分节点故障,一致性哈希能够较为平滑地调整key
的分布。但是, 如果节点在环上的分布不均,可能导致部分节点负责的key
较多。解决办法:引入虚拟节点(Virtual Nodes
),将每个物理节点映射到环上多个位置,提升整体均衡性。一致性哈希分区, 通过构建哈希环使得节点动态变更时只影响局部数据,适用于分布式缓存和大规模分布式存储,但需要注意节点分布的均衡性。 -
虚拟槽分区: 固定虚拟槽, 例如
Redis Cluster
就采用16384
个固定的哈希槽(virtual slots
)。每个key
通过CRC16
等哈希函数计算出一个槽编号。每个槽由一个物理节点负责存储,节点之间可以均衡分配槽。当节点发生变更时 (扩容、缩容),只需要将部分槽重新分配给新节点,而不是重新映射所有key
。虚拟槽分区 节点增减只会涉及到部分槽的重新分配,整体影响较小。通过预先划定固定数量的槽,可以较容易地将槽均匀分配给各个节点,避免热点问题。但是, 需要借助专门的管理工具或命令(如Redis Cluster
的rebalancing
命令)来进行槽的迁移和重新分配。虽然16384
个槽通常足够大,但在某些极端情况下可能仍需要调整槽的策略。虚拟槽分区, 以固定的虚拟槽为中间层,将数据映射与节点分配解耦,在 Redis Cluster 等系统中得到广泛应用,能够在节点变更时实现较平滑的负载调整。
-
-
问题
-
问题一: 由于数据被分布在不同节点上,
Redis Cluster
对涉及多个键的操作有一定限制。只有当多个键属于同一哈希槽时,才能保证操作在单个节点内完成,否则需要借助客户端进行协调。 -
问题二: 理想状态下,均匀的哈希分布能够保证每个桶(或每个槽、每个节点)上的数据量相对平衡。但如果键的命名不当,可能会出现热点问题,导致部分节点压力过大,这时可以考虑使用 哈希标签(例如
{user}:1001
)来保证相关键分布在同一槽中,或者调整键的设计以更好地分散负载。 -
问题三: 在集群模式下,加入新节点或移除节点时需要对哈希槽进行重新分配。虽然
Redis
提供了工具和机制来自动或半自动完成这项工作,但实际操作中需要注意数据迁移期间可能引起的短暂延迟和请求重定向。
-
-
-
故障转移: 集群中每个数据主节点(
master
)通常都会有一个或多个从节点(slave
)进行数据复制。当某个主节点发生故障时,集群能够自动将其从节点升级为新的主节点, 并更新集群内的槽映射,确保服务不中断, 保证数据服务的连续性。集群中主要有两类节点: 主节点(Master
), 负责存储数据以及处理客户端的读写请求; 从节点(Slave
), 用于复制主节点数据,当主节点失效时,从节点可以提升为新的主节点; 每个节点会定期通过心跳信息检测其他节点的状态。如果检测到某个主节点故障,并且达到故障判定阈值,集群会自动从该主节点的从节点中选举出新的主节点,完成故障转移。整个故障转移过程中,集群内部会更新哈希槽的映射信息,新Master
所负责的槽信息会广播给所有节点,确保客户端请求可以正确路由。集群内部有专门的消息传递机制(Cluster Bus
),用于广播槽变更、故障转移等关键信息。每次集群拓扑发生变化时,都会生成一个新的配置纪元,借此区分不同时间段的集群状态,防止旧的状态覆盖新的映射。 -
智能路由: 使用
Redis Cluster
感知客户端时,支持集群模式的客户端库能够自动根据键的哈希槽路由到正确的节点,无需人工干预。 -
无中心架构: 集群内所有节点平等对待,没有单一的中央控制点,节点之间采用
Gossip
协议相互通信,分担故障检测和集群状态的维护任务,从而消除了单点故障风险。Redis Cluster
节点之间采用Gossip
协议定期交换自身状态、槽映射、故障信息等,确保全局一致性。通过这种方式,节点能够迅速感知其他节点的上线、下线以及槽变化情况。
Redis Cluster
优点缺点: Redis Cluster
能够轻松通过增加节点扩展存储容量与处理能力, 自动故障转移保证了系统在部分节点失效时仍能正常运行, 避免了单点故障,整体系统更加稳定。但是, 多键操作受到哈希槽分布的限制,需要开发者特别注意; 相对于单机或哨兵模式,集群部署的复杂度更高,运维时需要注意节点间的数据均衡和槽迁移。
二、安装
假设你在一台或多台机器上部署多个 Redis
实例来构成一个集群(一般建议至少 6
个实例:3
个主节点 + 3
个从节点)
2.1 配置 Redis 实例
每个实例需要独立配置(建议使用不同的端口),并启用集群模式。可以基于 redis.conf
创建多个配置文
cp redis.conf redis-7000.conf
cp redis.conf redis-7001.conf
cp redis.conf redis-7002.conf
cp redis.conf redis-7003.conf
cp redis.conf redis-7004.conf
cp redis.conf redis-7005.conf
修改配置文件: 1. 启用集群模式; 2. 指定集群配置文件名称(该文件用于记录节点信息)3. 设置节点超时时间(毫秒)。对所有 6
个配置文件做类似修改,只需确保每个文件的端口和 cluster-config-file 文件名唯一。
port ${port}
daemonize yes
# 启用集群模式
cluster-enabled yes
logfile "${port}.log"
dir "/opt/redis/redis/data/"
dbfilename "dump-${port}.rdb"
# 设置节点超时时间(毫秒)
cluster-node-timeout 5000
# 指定集群配置文件名称(该文件用于记录节点信息)
cluster-config-file nodes-${port}.conf
2.2 启动 Redis 实例
分别启动每个 Redis
实例,举例如下(假设你在同一台机器上):
redis-server ./redis-7000.conf
redis-server ./redis-7001.conf
redis-server ./redis-7002.conf
redis-server ./redis-7003.conf
redis-server ./redis-7004.conf
redis-server ./redis-7005.conf
如果在多台机器上部署,则需保证各实例的网络互通,并确保防火墙放通相应端口(例如 7000-7005
以及集群总线端口)。
2.3 创建 Redis 集群
Redis
提供了内置工具来组建集群。使用 redis-cli
带上 --cluster create
参数:
redis-cli --cluster create 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 --cluster-replicas 1
--cluster-replicas
: 1
表示每个主节点分配 1
个从节点。
- 执行该命令后,工具会自动计算槽分配,并提示你输入 yes 来确认创建集群。
2.4 验证 Redis 集群
使用 redis-cli
连接任一节点后执行以下命令:
redis-cli -p 7000 cluster nodes
你应该能看到各个节点的角色(master/slave
)、负责的槽范围以及集群 ID
等信息。也可以执行:
redis-cli -p 7000 cluster info
查看集群整体状态,确保 cluster_state:ok
。
2.5 Redis 集群扩容
扩容的基本思路是将新节点加入集群,然后将部分哈希槽从现有节点迁移到新节点上,使数据均衡分布。
1. 添加新节点到集群: 假设你有一个正在运行的集群,通过以下命令将新节点(例如:127.0.0.1:7006
)加入到已有集群中(连接任意已存在的节点)。这样,新节点会加入集群,但初始状态下不会负责任何槽。
redis-cli --cluster add-node 127.0.0.1:7006 127.0.0.1:7000
2. 重新分配哈希槽(Reshard
): 使用 redis-cli --cluster reshard
命令将部分槽迁移到新加入的节点。
-
执行命令:
redis-cli --cluster reshard 127.0.0.1:7000
-
根据提示输入需要迁移的槽数量(例如
2000
个槽) -
输入目标节点的
ID
(新节点的ID
,可通过cluster nodes
命令获取)。 -
输入一个或多个源节点的
ID
,表示从哪些节点迁移槽。 -
最后确认操作(输入
yes
),工具会自动开始槽的迁移工作。
完成后,新节点将负责部分槽,从而实现扩容和负载均衡。
2.6 Redis 集群缩容
缩容时的目标是将要移除节点负责的槽重新迁移到其他节点,然后将该节点从集群中剔除。
1. 迁移槽数据: 假设要缩容的节点是 127.0.0.1:7006
,首先需要将该节点上负责的所有槽迁移到其他节点。可以使用 redis-cli --cluster reshard
命令:
- 执行:
redis-cli --cluster reshard 127.0.0.1:7000
-
输入要迁移的槽数量(通常可以迁移该节点全部槽,具体槽数可以通过
cluster nodes
查看)。 -
指定目标节点的
ID
(选择一个或多个其他节点)。 -
指定源节点的
ID
为待移除节点(例如127.0.0.1:7006
对应的节点ID
)。 -
确认操作后,工具会将该节点的槽迁移到其他节点上。
2. 删除节点: 在确认待移除节点上已经没有槽之后,执行删除操作
redis-cli --cluster del-node 127.0.0.1:7000 <node-id>
其中 <node-id>
为待删除节点(127.0.0.1:7006
)的 ID
,可以通过 cluster nodes
命令获得。该命令会将该节点从集群成员中移除。