目录
Redis集群方案
主从复制
主从复制的基本原理
主从复制的工作流程
乐观复制
主从复制的优势
哨兵机制
哨兵的关键作用
服务状态监控
哨兵选举Master规则
分片集群
分片集群中的数据读写
数据写入
数据读取
一致性哈希和客户端分片
Redis集群方案
微服务时代背景下,现实中我们的项目往往需要多台Redis服务器的支持:
-
结构上:单个Redis容易引发单点故障,一台服务器需要承载所有请求。所以需要多个节点同步复制。
-
容量上:单个Redis的内存很容易成为存储瓶颈。所以需要进行数据分片。
针对Redis的集群方案,常见的有:主从复制、哨兵模式、分片集群。
主从复制
Redis的主从复制是一种常见的数据复制和高可用性的机制。它通过将一个Redis节点的数据复制到其他节点,实现数据的备份和读写分离。
主从复制的基本原理
-
全量复制: 初始阶段,从节点会向主节点发送一次
SYNC
命令,主节点会将自己的数据库快照发送给从节点。从节点接收到快照后,会清空自己的数据库,然后将接收到的快照数据加载到自己的数据库中,完成全量复制。-
Replication Id:简称replid,是数据集的标记,id一致则说明是同一数据集。每一个master都有唯一的replid,slave则会继承master节点的replid。
-
offset:偏移量,随着记录在repl_baklog中的数据增多而逐渐增大。slave完成同步时也会记录当前同步的offset。如果slave的offset小于master的offset,说明slave数据落后于master,需要更新。
-
-
增量复制: 一旦从节点完成全量复制,主节点会继续将自己的写操作(写命令)发送给从节点。这样从节点就能保持自己的数据库和主节点保持一致。增量复制通过主节点将执行的写命令传播给从节点来实现。
主从复制的工作流程
-
从节点连接到主节点: 从节点通过发送
PING
命令或PSYNC
命令与主节点建立连接。PSYNC
命令是增量复制的一部分,它用于在断线重连时,从节点告知主节点自己的复制偏移量。 -
主节点创建快照: 一旦从节点连接到主节点,主节点会执行
BGSAVE
命令创建一个RDB快照。这个快照包含了当前主节点数据库的全部数据。 -
从节点加载快照: 主节点创建快照后,会将快照数据传输给从节点。从节点接收到快照后,会清空自己的数据库,然后加载主节点传来的快照数据,完成全量复制。
-
增量复制: 从节点完成全量复制后,主节点会将自己的写操作(写命令)发送给从节点。从节点接收到写命令后,会执行相同的写操作,保持与主节点数据的一致性。
-
心跳和断线重连: 主节点和从节点会通过心跳机制保持连接。如果从节点与主节点的连接断开,从节点会尝试重新连接,并使用
PSYNC
命令告知主节点自己的复制偏移量,主节点会根据从节点的复制偏移量发送缺失的写命令,从而实现断线重连时的增量同步。
乐观复制
乐观复制(Optimistic Replication)通常是指一种基于版本控制的复制策略,其核心思想是节点在进行数据复制时,不需要先获取到全局锁,而是通过版本号等机制来进行冲突的检测和解决。在Redis中,乐观复制并非官方的特性,但是可以通过一些手段实现类似的效果。
-
版本号: 在Redis中,你可以使用自定义的版本号,通过
INCR
等命令来增加节点的版本号。在写操作时,将写操作和对应的版本号发送给其他节点。 -
冲突检测: 在节点接收到写操作时,检查接收到的写操作的版本号是否比自己的版本号大。如果大,执行写操作并更新版本号;如果小,则发生冲突,需要进行冲突解决。
-
冲突解决: 冲突解决可以采用一些策略,例如最后一次写操作覆盖之前的写操作,或者保留所有冲突的写操作等。
需要注意的是,这种自定义的乐观复制机制需要谨慎设计,因为在分布式环境中,冲突的发生是不可避免的,需要考虑如何解决冲突、保证数据的一致性等问题。另外,Redis本身提供的主从复制和哨兵机制已经能够满足大部分应用场景,使用官方提供的复制机制通常更为稳妥。
主从复制的优势
-
数据备份: 主从复制可以用作数据的备份机制,当主节点发生故障时,从节点可以升级为主节点,确保数据的可用性。
-
读写分离: 从节点可以处理读请求,实现读写分离,分担主节点的读负载,提高系统的并发处理能力。
-
容灾性: 当主节点发生故障时,可以快速切换到从节点,提高系统的容灾性。
-
故障恢复: 当主节点恢复正常工作时,可以将从节点重新配置为主节点的从节点,实现故障恢复。
哨兵机制
Redis提供了哨兵(Sentinel)机制:哨兵通过协调多个哨兵进程的工作来实现主从集群的自动故障恢复。
哨兵的关键作用
-
监控: 哨兵负责监控Redis主节点和从节点的健康状态。它通过周期性的检查节点的状态,包括网络连接状态、是否存活等。
-
故障检测: 当哨兵发现某个Redis节点不可用时,它会将这一信息广播给其他哨兵和客户端。哨兵根据一定的条件来判断一个节点是否处于不可用状态,例如在规定时间内未响应等。
-
故障转移: 在主节点不可用时,哨兵会自动进行故障转移。它会通过选举的方式选择一个从节点升级为新的主节点,保证系统的可用性。
-
自动发现: 哨兵可以通过
SENTINEL
命令进行自动发现,不需要手动配置所有哨兵的信息。这使得系统更加灵活,能够适应节点的动态变化。
服务状态监控
Sentinel基于心跳机制监测服务状态,每隔1秒向集群的每个实例发送ping命令:
-
主观下线:如果某sentinel节点发现某实例未在规定时间响应,则认为该实例主观下线。
-
客观下线:若超过指定数量(quorum)的sentinel都认为该实例主观下线,则该实例客观下线。quorum值最好超过Sentinel实例数量的一半。
哨兵选举Master规则
Redis哨兵进行主节点选举时,遵循一定的规则和条件,以确保选出的新主节点是可用的。以下是哨兵选举Master的基本规则:
-
Quorum(仲裁):
-
选举需要达到Quorum,Quorum的计算方式为
(哨兵总数 / 2) + 1
。只有得到足够多的哨兵投票,选举才能生效。 -
Quorum的引入是为了防止因为网络分裂等问题导致多个主节点同时被选举。
-
-
哨兵优先级:
-
每个哨兵节点都有一个配置的优先级,可以通过
sentinel.conf
文件中的sentinel myid priority
配置项设置。默认为100。 -
哨兵在选举时会选择优先级最高的哨兵作为领导者,由领导者发起主节点选举。
-
-
最高优先级的Slave:
-
如果有多个从节点具有相同的优先级,哨兵将选择复制偏移量(replication offset)最大的从节点作为新的主节点。
-
复制偏移量是指从节点的复制进度,选择复制偏移量最大的从节点有助于保持数据的一致性。
-
-
选举超时和投票:
-
当一个哨兵发现主节点不可用后,它会发起一轮选举,设定一个选举超时时间。在这个时间内,哨兵会收集其他哨兵的投票。
-
哨兵通过相互通信,进行选举投票,包括对自身的投票。哨兵节点可以投票给自己,也可以投票给其他哨兵。
-
-
领导者发起选举:
-
选举开始时,哨兵中优先级最高的节点成为领导者,由领导者发起主节点选举。
-
领导者向其他哨兵广播自己的选票,其他哨兵在收到选票后进行投票。
-
-
多轮投票:
-
选举可能进行多轮,直到某个哨兵得到Quorum的支持为止。在每一轮选举中,哨兵都会尝试发起并参与投票。
-
-
新主节点确认:
-
当一个哨兵获得Quorum的支持后,它会确认选出的新主节点。其他哨兵节点收到确认后,会更新配置并将新的主节点信息广播给其他节点。
-
-
客户端更新:
-
一旦新的主节点选出,哨兵将通知客户端更新连接信息,使客户端连接到新的主节点。
-
通过这些规则,Redis哨兵确保了在主节点发生故障时,能够选出一个优先级高、复制偏移量最大的从节点作为新的主节点,从而确保系统的高可用性。
分片集群
主从和哨兵可以解决高可用、高并发读的问题。但是依然有两个问题没有解决:
-
海量数据存储问题
-
高并发写的问题
使用分片集群可以解决上述问题,分片集群特征:
-
集群中有多个master,每个master保存不同数据
-
每个master都可以有多个slave节点
-
master之间通过ping监测彼此健康状态
-
客户端请求可以访问集群任意节点,最终都会被转发到正确节点
分片集群中的数据读写
Redis分片集群使用了哈希槽(Hash Slot),Redis分片集群将整个数据集划分为16384个哈希槽,每个槽都有一个唯一的编号。这些哈希槽用于存储数据,同时也用于管理数据的分片。数据的键通过哈希算法映射到某个哈希槽上,决定了数据应该由哪个节点负责存储。
数据写入
-
计算哈希槽:
-
当客户端向Redis集群发送写入请求时,Redis首先计算键对应的哈希槽。通过哈希算法,确定键属于哪个哈希槽。
-
-
选择节点:
-
哈希槽确定后,根据哈希槽和集群中的节点分布,选择负责处理该哈希槽的节点。这个节点被称为槽的持有者。
-
-
发送写请求:
-
客户端将写请求发送给被选择的节点。如果该节点是主节点,它将负责处理写请求;如果是从节点,写请求将被转发给主节点。
-
-
写入数据:
-
主节点接收到写入请求后,将数据写入自己的数据存储。然后,主节点会将写入操作通过集群总线广播给其他从节点,以保持数据的一致性。
-
数据读取
-
计算哈希槽:
-
当客户端向Redis集群发送读取请求时,同样需要计算键对应的哈希槽,确定数据所在的槽。
-
-
选择节点:
-
根据哈希槽和节点分布,选择负责处理该哈希槽的节点。这个节点可能是主节点,也可能是从节点。
-
-
发送读请求:
-
客户端将读请求发送给被选择的节点。如果是主节点,它将直接处理读请求;如果是从节点,读请求可能会被转发到主节点。
-
-
读取数据:
-
节点处理读请求,如果是主节点,直接从自己的数据存储中读取数据并返回给客户端;如果是从节点,它可能会从主节点获取最新的数据并返回给客户端。
-
一致性哈希和客户端分片
-
为了在节点变动时保持哈希槽的一致性,Redis使用一致性哈希算法。这确保了在节点加入或退出时,只有少量的槽需要迁移,而不是整个数据集。
-
客户端也需要进行分片,以确保请求被正确发送到负责处理对应数据分片的节点。通常,客户端使用一致性哈希算法或其他分片策略来选择正确的节点。
通过上述过程,Redis分片结构实现了数据的分散存储和读写负载均衡,同时通过哈希槽的管理和一致性哈希算法保障了系统的一致性和可用性。