目录
1. 全量同步(Full Resynchronization)
2. 增量同步(Incremental Replication)
1.Redis 集群搭建有几种模式
-
主从复制(Replication)模式:
- 在这种模式下,一个节点被配置为主节点(Master),负责处理所有的写操作以及部分读操作。其他节点作为从节点(Slave),通过复制主节点的数据来保持数据的副本。
- 主节点将写操作产生的数据更改同步到从节点,通常采用异步复制方式,但也支持半同步复制以提高数据安全性。
- 从节点通常是只读的,用于分摊主节点的读取压力,提供数据冗余,以及在主节点故障时提供故障恢复的基础。
-
哨兵(Sentinel)模式:
- Sentinel 是 Redis 官方提供的高可用性解决方案,它由一组独立运行的 Sentinel 进程组成,这些进程监控 Redis 主从集群的健康状态。
- Sentinel 能够自动检测主节点故障,并通过协商选举出新的主节点,同时通知从节点更新其复制关系,实现故障转移(Failover)过程。
- Sentinel 还提供了客户端通知功能,使得应用程序能够及时得知主节点的变化,从而自动调整连接到正确的节点。
-
Cluster(集群)模式:
- Redis Cluster 是官方提供的分布式数据存储方案,通过将数据分片(Sharding)分布在多个节点上,实现水平扩展和数据的高可用性。
- 在 Cluster 模式中,每个节点都可以是主节点,处理一部分数据的读写操作。节点间通过 gossip 协议自动发现和维护彼此间的连接及数据分布状态。
- 数据分片通过哈希槽(Hash Slot)的概念实现,客户端使用一致性哈希算法将键映射到特定的哈希槽,再由 Redis Cluster 负责将请求路由到对应的主节点。
- Cluster 模式还支持数据的自动 rebalancing,当添加或移除节点时,能够重新分配哈希槽以保持数据分布的均衡。
2.Redis 主从复制的实现
主从复制可以根据需要分为全量同步的增量同步两种方式。
-
全量同步
Redis 全量复制一般发生在 slave 的初始阶段,这时 slave 需要将 master 上的数据都复制一份,具体步骤如下:
- slave 连接 master,发送 SYNC 命令;
- master 接到 SYNC 命令后执行 BGSAVE 命令生产 RDB 文件,并使用缓冲区记录此后执行的所有写命令;
- master 执行完 BGSAVE 后,向所有的 slave 发送快照文件,并在发送过程中继续记录执行的写命令;
- slave 收到快照后,丢弃所有的旧数据,载入收到的数据;
- master 快照发送完成后就会开始向 slave 发送缓冲区的写命令;
- slave 完成对快照的载入,并开始接受命令请求,执行来自 master 缓冲区的写命令;
- slave 完成上面的数据初始化后就可以开始接受用户的读请求了。
-
增量同步
增量复制实际上就是在 slave 初始化完成后开始正常工作时 master 发生写操作同步到 slave 的过程。增量复制的过程主要是 master 每执行一个写命令就会向 slave 发送相同的写命令,slave 接受并执行写命令,从而保持主从一致。
3.Redis 的主从同步策略
1. 全量同步(Full Resynchronization)
当从节点初次连接主节点或断线后重新连接且无法进行部分同步时,主节点执行 BGSAVE
命令生成 RDB 快照,并将其发送给从节点。从节点加载 RDB 快照以快速恢复大部分数据。接着,主节点将断线期间积累的写命令(存储在复制积压缓冲区中)发送给从节点,从节点执行这些命令以达到与主节点的完全同步。
2. 增量同步(Incremental Replication)
-
部分同步(Partial Resynchronization):一旦从节点完成全量复制并进入正常工作状态,主从节点之间采用增量复制进行持续同步。主节点在执行完客户端的写操作后,将这些命令通过命令传播机制发送给所有从节点。从节点接收到命令后在本地执行,保持与主节点的实时同步。
-
复制偏移量(Replication Offset):主从节点各自维护一个复制偏移量,表示已复制命令的字节数。每当主节点向从节点发送一个命令时,双方都会更新自己的偏移量。通过比较偏移量,主从节点可以判断复制进度和是否存在数据差距。
-
复制积压缓冲区(Replication Backlog):主节点维护一个固定长度的先进先出(FIFO)队列,存储最近一段时间的写命令。当从节点断线重连时,如果复制积压缓冲区中仍保留了断线期间的命令,从节点可以请求这部分命令进行部分同步,避免进行耗时的全量复制。
-
4.Redis一致性hash
一致性 hash 其实是普通 hash 算法的改良版,其 hash 计算方法没有变化,但是 hash 空间发生了变化,由原来的线性的变成了环。
缓存 key 通过 hash 计算之后得到在 hash 环中的位置,然后顺时针方向找到第一个节点,这个节点就是存放 key 的节点。
基本原理
-
哈希环:将整个哈希值空间组织成一个虚拟的环状结构,通常是一个0到2^32-1的整数环。每个节点(Redis 实例)和数据键(key)都通过相同的哈希函数映射到这个环上。
-
节点映射:每个 Redis 节点通过其标识符(如 IP 地址、端口号或节点ID)计算出一个哈希值,将该值映射到哈希环上。在 Redis Cluster 中,使用的是“哈希槽(hash slot)”的概念,将整个哈希环划分为固定数量(如16384个)的槽位,每个节点负责一部分槽位。
-
数据映射:对于要存储或查询的数据键,同样通过哈希函数计算其哈希值,并定位到哈希环上的一个位置。然后沿着环顺时针方向查找第一个遇到的节点(或槽位),该节点即为该数据键应当存储或查询的目标节点。
节点动态管理
-
节点加入:当新增一个节点时,它会接管一部分哈希槽。这些槽位可能是从其他节点转移过来的,也可能是在创建集群时预先分配给新节点的。由于每个槽位对应的数据范围是确定的,因此只需要将原来映射到这些槽位上的数据迁移到新节点即可,影响范围有限。
-
节点移除或故障:当节点离开集群或发生故障时,其负责的哈希槽需要被其他节点接管。由于数据已经按照槽位均匀分布,其他节点只需接管对应的槽位,就能继续提供服务,受影响的数据同样仅限于那些槽位对应的范围。
虚拟节点
为了进一步提高数据分布的均匀性和应对节点性能差异,Redis Cluster 在实现中通常会使用虚拟节点(Virtual Node)的概念。每个物理节点对应多个虚拟节点,每个虚拟节点有自己的哈希值并映射到哈希环上。这样,即使实际节点数量较少,也能通过虚拟节点在环上形成更为均匀的分布,从而减少数据迁移时的波动。
客户端支持
Redis 客户端在连接集群时,通常会获取集群的元数据(节点列表和槽位分配信息),并内置一致性哈希逻辑。客户端在执行命令时,会根据命令涉及的键计算哈希值,确定相应的哈希槽,并直接连接到负责该槽位的节点进行操作。这样,客户端能够透明地处理节点间的路由,无需应用程序显式处理节点分配。
总结
Redis 中的一致性哈希主要体现在 Redis Cluster 架构中,通过将数据键和节点映射到同一个哈希环上,并结合哈希槽和虚拟节点技术,实现了数据在节点间的均衡分布和高效路由。这种设计使得 Redis 集群在面对节点动态增删时,能够最小化数据迁移,保持服务的高可用性和数据的高一致性。客户端通过内置一致性哈希逻辑,能够自动寻址并直接与正确节点交互,简化了应用程序的开发和维护。
5.Cluster 模式的原理
实现原理就是一致性 Hash。Redis Cluster 中有一个 16384 长度的槽的概念,他们的编号为 0、1、2、3 …… 16382、16383。这个槽是一个虚拟的槽,并不是真正存在的。正常工作的时候,Redis Cluster 中的每个 Master 节点都会负责一部分的槽,当有某个 key 被映射到某个 Master 负责的槽,那么这个 Master 负责为这个 key 提供服务。
至于哪个 Master 节点负责哪个槽,这是可以由用户指定的,也可以在初始化的时候自动生成(redis-trib.rb脚本)。这里值得一提的是,在 Redis Cluster 中,只有 Master 才拥有槽的所有权,如果是某个 Master 的 slave,这个slave只负责槽的使用,但是没有所有权。
6.Cluster 的分片机制
Redis Cluster 的分片机制基于哈希槽的概念,通过将键映射到特定的槽位,并将槽位分配给不同的节点,实现了数据在集群内的均匀分布和高效路由。
7.客户端如何路由
Redis 集群中的数据是分片存储的,那我们该如何知道某个 key 存在哪个节点上呢?即我们需要一个查询路由,该路由根据给定的 key,返回存储该键值的机器地址。
Redis 的每个节点中都存储着整个集群的状态,集群状态中一个重要的信息就是每个桶的负责节点。在具体的实现中,Redis 用一个大小固定为 CLUSTER_SLOTS 的 clusterNode 数组 slots 来保存每个桶的负责节点。
在集群模式下,Redis 接收任何键相关命令时首先计算键对应的桶编号,再根据桶找出所对应的节点,如果节点是自身,则处理键命令;否则回复 MOVED 重定向错误,通知客户端请求正确的节点,这个过程称为 MOVED 重定向。重定向信息包含了键所对应的桶以及负责该桶的节点地址,根据这些信息客户端就可以向正确的节点发起请求。
8.Redis 为什么是16384个槽位
消息体大小上的考量
至于这个消息体有多大?显然最占空间的就是 myslots 数组:16384÷8÷1024=2kb
。如果槽位达到 65536,则所占空间提升到 65536÷8÷1024=8kb
,极大浪费带宽。
均衡数据分布
在预期的集群规模下(通常不超过几千个节点),16384 个槽位可以确保每个节点负责的槽位数量适中,从而实现数据在节点间的相对均匀分布。即使在节点数量较小(如几十个)的情况下,每个节点也能平均分配到数百至数千个槽位,有利于保持数据分布的均衡。
行业惯例与经验
Redis Cluster 的设计者可能参考了其他分布式系统的实践经验,16384 作为常用的一致性哈希槽位数量,已经在其他系统中得到了验证,具有较好的实践效果和社区共识。
9.集群的故障发现与迁移
故障发现(Failure Detection)
主观下线(PFail)
- 节点间心跳检测:每个 Redis Cluster 节点周期性地向其他节点发送
PING
消息,以检查它们是否在线。如果一个节点在一定时间内(默认超时时间通常是几百毫秒)没有收到另一个节点的响应,它会标记该节点为 主观下线(Subjectively Down, PFail)。这只是单个节点对另一个节点状态的局部判断。
客观下线(Fail)
-
Gossip 协议传播:主观下线状态通过集群内部的 Gossip 协议迅速传播给其他节点。当半数以上的节点(包括自身)都认为某个节点 PFail 时,该节点被宣布为 客观下线(Objectively Down, Fail)。这是整个集群对节点状态达成的共识。
-
故障确认:为了防止短暂网络波动导致的误判,客观下线通常还伴随着额外的确认步骤,比如多次探测失败、等待一段时间等条件。
故障迁移(Failover)
一旦一个主节点被标记为客观下线,集群会触发故障迁移过程以恢复服务的高可用性。故障迁移通常包括以下几个步骤:
从节点选举
-
候选资格:从主节点的从节点(如果有)中选择一个作为新的主节点候选人。通常会选择复制偏移量(replication offset)最高、数据最接近最新的从节点,以减少数据丢失和不一致的风险。
-
选举触发:选举可以由集群中的其他节点(如哨兵节点,如果部署了哨兵模式)或从节点自己(某些情况下)发起。选举过程可能涉及投票或其他协调机制,以确定哪个从节点应晋升为主节点。
主节点替换
-
晋升为主节点:被选中的从节点执行
slaveof no one
命令,断开与原主节点的复制关系,将自己的角色提升为新的主节点。 -
数据接管:由于从节点一直在与原主节点保持同步,因此晋升为主节点后,它已经拥有大部分(如果不是全部)最近的数据。此时,客户端开始向新主节点发送请求。
通知客户端与数据迁移
-
通知客户端:新主节点通过发送
MOVED
响应告知客户端其新的位置。客户端收到后,更新其内部的槽位映射表,将后续对该槽位的请求直接发送到新主节点。 -
数据迁移:对于原主节点在下线前未同步给新主节点的少量新写入数据,可能需要通过异步复制或客户端重试等方式进行恢复。同时,其他从节点也会开始与新主节点建立复制关系,以保持数据一致性。
原主节点恢复
-
角色调整:当原主节点恢复在线时,它会发现自己不再是主节点,而是变成了从节点。它会自动与新主节点建立复制关系,开始同步数据。
-
槽位重分配:在某些情况下,原主节点可能需要重新获得部分或全部槽位的分配权,这通常需要手动干预或集群管理工具的支持。
总的来说,Redis Cluster 的故障发现与迁移机制旨在快速检测并响应节点故障,通过自动或半自动的方式将服务从故障节点转移至健康的节点,确保数据的高可用性和服务的连续性。在整个过程中,集群内部通过 gossip 协议、心跳检测和角色转换等手段维持数据的一致性和服务的稳定性。