Advertisement

动力节点最全笔记Redis7-第五章Redis主从集群

阅读量:

5 Redis主从集群

为防止Redis发生单一故障而设计其架构以确保系统的可靠性与稳定性。建议构建一个 Redis 集群系统用于数据的一致性存储与高可用性配置。当任何一个 Redis 节点发生故障时,剩余的节点会接管负载,确保系统的连续运行。

5.1 主从集群搭建

Redis采用了一种'一主多从'架构来实现分布式缓存服务中的'读写分离'模式。在该架构中, Master节点不仅负责接收并处理客户端的所有访问请求,还需确保这些请求能够高效地被分配给合适的 Slave 节点进行响应,同时 Master 节点还承担着数据持久化存储的任务。Slave 节点则仅限于接收并处理客户端的数据读取请求,以减少对 Master 节点的压力,从而提高系统的整体性能和稳定性。这一模式之所以被采用的主要原因在于,数据库集群通常面临较高的数据 write 操作压力,而这种压力往往源于大量并发的数据插入操作以及复杂的事务管理需求;因此,在这种设计下只需确保有一个专门用于 write 操作的数据存储节点即可完成所有的 write 请求。

5.1.1 伪集群搭建与配置

当使用单线程IO模型时,在提升处理器利用率的前提下,在同一个主节点上部署多台Redis服务器来构建 Redis 主从伪集群;另一种常见场景是在学习 Redis 的情况下,默认资源有限的情况下无法启动多个虚拟机环境;用于搭建读写分离式的伪集群,则会采用一个 Master 节点搭配两个 Slave 节点;它们的具体端口号分别为:6380、6381 和 6382。

5.1.1.1 复制redis.conf

在Redis安装目录内创建一个目录无需指定特定名称命名为'cluster'随后将Redis配置文件复制至该目录下随后的配置处理会引用此主配置因此在该Redis节点的配置中需统一设置相同的公共属性参数

5.1.1.2 修改redis.conf

在redis.conf中做如下几项修改:

5.1.1.2.1 masterauth

因为我们在构建主从集群时,默认情况下每个主机都有可能充当Master角色,在这种情况下建议不要配置requirepass属性以避免潜在的安全风险。如果确实需要进行requirepass配置,则必须确保所有主机使用相同的统一密码进行安全认证。具体操作中需要注意以下几点:首先,在所有的配置文件中都需要同时定义并启用两个完全相同的属性——requirepassmasterauth这两个参数都是针对同一个主节点而言的重要安全机制。其中requirepass参数用于指定当前节点(通常是Master)的安全认证密钥值;而masterauth参数则用于指定当某个Slave节点试图连接到该Master节点时所提交的安全认证密钥值以供Master节点进行身份验证确认自己是否具备合法权限的操作。

5.1.1.2.2 repl-disable-tcp-nodelay

该属性用于指定TCP特性tcp-nodelay是否被禁用。当将其设置为"yes"时,则会禁止tcp-nodelay特性,在这种情况下, master与slave之间的通信将经历延迟,但所使用的TCP包数量相对较少,从而导致较低的网络带宽消耗.反之,若将其设置为"no",则通信延迟将减小,然而所使用的TCP包数量将增加,相应地占据更大的网络带宽.

tcp-nodelay:为了最大化地利用网络带宽,在这种机制下(称为tcp-nodelay),send-order会不断地向接收端发送尽可能大的数据包。其工作原理为:当接收方接收到需要传输的数据时(通常是请求头信息),并不立即发送该数据包到主传输链路中去(即不立即发送),而是等到该数据包的大小达到一定规模时(这一规模由tcp协议定义),才会一次性将所有这些待传输出去。这种方法使得主传输链路中的有效负载比例得到了显著提升,在线等待的时间也相应减少了一定的比例(具体数值由tcp协议参数决定)。这样做的结果就是最大限度地提高了网络利用率,并减少了资源浪费的可能性。

tcp-nodelay则是TCP协议中Nagle算法的开头。
5.1.1.3 新建redis6380.conf

新建一个redis配置文件redis6380.conf,该配置文件中的Redis端口号为6380。

5.1.1.4 再复制出两个conf文件

再次利用redis6380.conf生成两个新的conf配置文件: redis6381.conf与redis6382.conf,并对这两个新生成的配置文件进行内容上的调整

修改redis6381.conf的内容如下:

修改redis6382.conf的内容如下:

5.1.1.5 启动三台Redis

分别由redis6380.conf、redis6381.conf与redis6382.conf三个配置文件被用来启动三台Redis。

5.1.1.6 设置主从关系

启动三个会话框并分别通过客户端程序依次建立与三台Redis服务器的连接;接着利用slaveof命令配置6380号Redis节点为主从节点。

5.1.1.7 查看状态信息

通过info replication命令可查看当前连接的Redis的状态信息。

5.1.2 分级管理

当Redis主从集群中的slave数量较多时,slave的数据同步环节对master造成了较大的性能压力.此时可采取优化措施对slave进行分级管理

该方案的操作步骤较为简单:只需让低层级的_SLAVE指定其slaveof参数指向其上级_SLAVE即可完成配置。值得注意的是,在此配置下,并不会影响到父级_SLAVE的状态——它仍然会保持为_SLAVE状态;然而实际上它是连接到了更高级别的_SLAVE节点。例如,在实际操作中,请确保所有相关参数正确无误地进行配置以达到预期效果。

此时会发现,Master的Slave只有6381一个主机。

5.1.3 容灾冷处理

在Redis集群中采用主从架构时,在主节点故障时该怎么办?主要有两种处理方案:一种是手动进行角色转换(即主从互换),将从节点切换为主节点(称为冷处理);另一种是启用哨兵模式(即热处理),实现集群高可用性的自动切换功能。当任何一个主从节点出现故障时,默认情况下都会将未接收到心跳信号的那个从节点升级为主节点(称为 slaveof no one)。如果该从节点原本已经有一个下级从节点存在,则它会直接被指定为主节点;而原来的主节点则会失去这个副节点的角色。

5.2 主从复制原理

5.2.1 主从复制过程

当Redis从属节点(slave node)接收到类似于slaveof 127.0.0.1 6380的命令时,在其能够从master持续复制数据的过程中大体经历了以下几个步骤:

5.2.1.1 保存master地址

当slaves响应 slaveof 指令时, 他们会迅速将新 master 的地址记录下来.

5.2.1.2 建立连接

在slaves中负责执行一个定时任务的任务,该定时任务旨在通过socket与master进行通信.在尝试与 master 建立 socket 连接时,若未能成功,则该定时任务将定期重新 attempt the connection.这一过程将持续进行,直至 either the connection is successful 或 it receives a 'slaveof no one' command.

5.2.1.3 slave发送ping命令

在连接建立成功之后,slave将发起ping命令以完成初步确认。若出现无响应情况,则该从站将终止与主节点的绑定关系,并在后续定时任务周期中重新创建新连接。

5.2.1.4 对slave身份验证

当master接收到slave发出的ping命令时,并不会立即作出回应。相反,在完成身份验证之前 master 会保持沉默。若身份验证失败,则会发送拒绝连接的信息;若身份验证成功,则向slave发送连接成功的确认信息。

5.2.1.5 master持久化

在首次通信取得成功之后,在接到来自slave的数据同步请求后, master将启动一个子进程,并指令该子进程中断当前操作并立即执行数据持久化处理。随后, master将等待该操作的完成, 并在此期间继续处理其他事务.

5.2.1.6 数据发送

当持久化完成时,master会启动一个子进程,该子进程中采用非阻塞模式向slave发送所需的数据.slave接收到这些数据后会持续地将其存储到本地持久化的文件中.在 slave 的数据同步过程中,master的主进程中持续接收客户端的新数据请求,不仅将这些新内容存储到 master 内存里,而且也会把这些新增的内容保存到共享缓存里.一旦 master 的持久化文件中的所有信息传输完毕之后,master 会立即向 slave 发送最新的共享缓存内容.当 master 完成这一操作后,slave 将其接收的数据更新到本地持久化的存储区域里.至此,整个复制过程便完成了.

5.2.1.7 slave恢复内存数据

一旦slave完成与master的数据同步,则会随后读取本地存储的持久化文件并将其恢复至本地内存空间之后即可对外提供读取服务。

5.2.1.8 持续增量复制

在 slave 外部提供的服务过程中,在 master 持续不断地将新增的数据通过增量的方式传递给 slave 以便维持主从两端的一致性

5.2.2 数据同步演变过程

5.2.2.1 sync同步

在Redis 2.8版本之前,在slave初次通信成功后(随后),slave会向master发起同步数据请求(即同步数据请求)。在此过程中(即在此阶段),master会将所有数据发送给slave(后者则将其保存在其本地持久化文件中)。这一过程被称为全量复制(即完成一次全量同步)。然而,在这个过程中存在一个潜在的问题:即当网络出现抖动导致复制操作中断时(出现故障的情况),当后续网络恢复并 slave与 master重新建立连接后(恢复连接状态),slave会再次发送同步请求,并从头开始执行全量复制流程(即重新启动一次完整的全量同步)。由于该过程耗时较长(整个操作周期较长),因此可能出现因网络波动导致的长时间无法完成同步的情况(长时间卡死的可能性较高)。此外,在此阶段中出现网络抖动的概率较高(可能导致长时间无法完成操作的情况的概率显著增加),并且一旦发生断层情况,则会导致从新开始执行全量复制所需的时间和资源消耗大且网络带宽需求高

5.2.2.2 psync同步

在Redis 2.8版本及以上版本中,全量复制采用了称为PSYNC的部分同步机制。在全量复制过程中因网络波动导致的数据传输中断发生时,在重新建立连接后就可以实现"断点续传"。具体而言,在断开位置处恢复数据传输操作的同时无需再从头开始进行拷贝操作。这一改进措施显著提升了系统的运行效率和稳定性。为了实现PSYNC机制这一目标,整个系统进行了三项主要改动:首先优化了数据存储结构以提升读取效率;其次通过改进算法降低了单次读取的数据延迟;最后强化了对数据库一致性与可用性的保障措施。

5.2.2.2.1 复制偏移量

系统对所有需要传输的数据进行了编码处理,在数据传输过程中按照顺序给每一个数据块分配了一个唯一的唯一编码值(其编码起始值设定为零)。这些唯一编码值被称作复制偏移索引(replica offset index),用于标识数据块在存储阵列中的具体位置关系。主节点及其副本节点均负责记录这一复制偏移信息。

每当 master 发送一个字节的数据后就会进行累计操作。统计信息可通过 info replication 的 master_repl_offset 属性获取。此外, slave 节点会定期向 master 节点报告其自身的复制偏移量完成情况,而 master 节点也会相应地记录这些副本位移量 offset 值。

每当 master 发送给 slave 数据时, slave 会累积这些数据中的偏移量. 通过 info replication 系统中的 slave_repl_offset字段可以获取统计数据.

5.2.2.2.2 主节点复制ID

一旦 master 启动后会立即开始生成一个长度为40位的十六进制字符串作为当前 master 的复制标识符

5.2.2.2.3 复制积压缓冲区

当 master 连接到 slave 时,在 master 中就会建立并持续维护一个队列 backlog,默认容量为 1MB(称为 copy buffer)。每当 master 接收到 write 操作的数据时(即 master 接收到 write operation data),它不仅会将这些数据存储到 master 主存中,并且还会将这些数据分配到每个 slave 对应的 send cache 中;此外还会将这些数据存储到 copy buffer 中。其作用就是用来保存最近的操作数据,在发生 "point-in-time recovery" 事件时用于进行数据补偿以防止丢失。

5.2.2.2.4 psync同步过程

psync是一个由slave提交的命令,其格式为psync <master_replid> <repl_offset>,表示当前slave要从指定的master中的repl_offset+1处开始复制。repl_offset表示当前slave已经完成复制的数据的offset。该命令保证了“断点续传”的实现。
在第一次开始复制时,slave并不知道master的动态ID,并且一定是从头开始复制,所以其提交的psync命令为 PSYNC ? -1。即master_replid为问号(?),repl_offset为-1。
如果复制过程中断后slave与master成功连接,则slave再次提交psyn命令。此时的psyn命令的repl_offset参数为其前面已经完成复制的数据的偏移量。

复制代码
    其实,并不是slave提交了psyn命令后就可以立即从master处开始复制,而是需要master给出响应结果后,根据响应结果来执行。master根据slave提交的请求及master自身情况会给出不同的响应结果。响应结果有三种可能:
    
    
      
    
    代码解读
  • FULLRESYNC <master_replid> <repl_offset>:通知slaves当前master的动态ID以及可以立即启动全量同步。这里的repl_offset一般为0
    • CONTINUE:通知slaves可以按照指定的位置继续上传数据。
    • ERR:当 master 的版本低于Redis 2.8时, 不支持PSYN操作, 此时可立即执行全量同步.
5.2.2.2.5 psync存在的问题
  • 在psync数据同步流程中,当slave发生重启时,其内存中所存储的 master节点动态ID以及后续传输偏移信息都会消失,因此"断点续传"操作无法继续执行,此时只能执行全量复制操作以恢复数据完整性。
  • 在psync数据同步流程中,当master出现故障后,slice会切换到新的 master节点进行服务接管,从而导致 slave节点必须从新 master节点重新执行全量复制过程,造成资源浪费。
5.2.2.3 psync同步的改进

Redis 4.0对psync进行了改进,提出了“同源增量同步”策略。

5.2.2.3.1 解决slave重启问题
复制代码
    针对“slave重启时master动态ID丢失问题”,改进后的psync将master的动态ID直接写入到了slave的持久化文件中。
    
    
      
    
    代码解读

slave重启后立即自本地持久化文件中读取master的动态ID值,并向master发起获取复制偏移量的请求。当master接收到该请求时,它会根据请求中的slave地址,在其内部存储中查找对应的复制偏移量,并将FULLRESYNC <master_replid> <repl_offset>的响应返回给slave以指示其当前传输起始位置。随后(master)执行断点续传操作

5.2.2.3.2 解决slave易主问题
复制代码
    slave易主后需要和新master进行全量复制,本质原因是新master不认识slave提交的psync请求中“原master的动态ID”。如果slave发送 PSYNC <原master_replid> <repl_offset>命令,新master能够识别出该slave要从原master复制数据,而自己的数据也都是从该master复制来的。那么新master就会明白,其与该slave“师出同门”,应该接收其“断点续传”同步请求。
    
    
      
    
    代码解读

在新 master 中确实存储着"原 master 的动态 ID"。改进后的 psync 系统使得每个从属节点都本地维护了当前 master 的 dynamic ID 信息。因此,在从属节点晋升为新的 master 后期, 其 local 仍然保有着前 master 的 dynamic ID. 这也正是解决"slave 转主"这一问题的关键所在. 通过 master 的 info replication 中, 我们可以通过 master_replid2 查看相关信息. 如果尚未发生过转主, 则该值仍保有 40 个零.

5.2.2.4 无盘操作
复制代码
    Redis 6.0对同步过程又进行了改进,提出了“无盘全量同步”与“无盘加载”策略,避免了耗时的IO操作。
    
    
      
    
    代码解读
  • 无盘全量同步:master的主进程child process fork生成一个子进程(即 master 的 child process),该子进程直接将内存中的数据传输至 slave 系统,并无需借助硬盘介质完成这一操作。
  • 无盘加载:当 slave 收到 master 发送的数据时(即 master 发送给 slave 的数据包),该系统无需将其存储至 disk file 而是直接加载到内存中,并因此能够迅速完成数据恢复。
5.2.2.5 共享复制积压缓冲区
复制代码
    Redis 7.0版本对复制积压缓冲区进行了改进,让各个slave的发送缓冲区共享复制积压缓冲区。这使得复制积压缓冲区的作用,除了可以保障数据的安全性外,还作为所有slave的发送缓冲区,充分利用了复制积压缓冲区。
    
    
      
    
    代码解读

5.3 哨兵机制实现

5.3.1 简介

对于Master宕机后的冷处理方式是无法实现高可用的。Redis从2.6版本开始提供了高可用的解决方案—— Sentinel哨兵机制。在集群中再引入一个节点,该节点充当Sentinel哨兵,用于监视Master的运行状态,并在Master宕机后自动指定一个Slave作为新的Master。整个过程无需人工参与,完全由哨兵自动完成。
不过,此时的Sentinel哨兵又成为了一个单点故障点:若哨兵发生宕机,整个集群将瘫痪。所以为了解决Sentinel的单点问题,又要为Sentinel创建一个集群,即Sentinel哨兵集群。一个哨兵的宕机,将不会影响到Redis集群的运行。
那么这些Sentinel哨兵是如何工作的呢?Sentinel是如何知道其监视的Master状态的呢?每个Sentinel都会定时会向Master发送心跳,如果Master在有效时间内向它们都进行了响应,则说明Master是“活着的”。如果Sentinel中有quorum个哨兵没有收到响应,那么就认为Master已经宕机,然后会有一个Sentinel做Failover故障转移。即将原来的某一个Slave晋升为Master。

5.3.2 Redis高可用集群搭建

在预算充足的条件下,在不差钱的情况下

角色 端口号 角色 端口号
master 6380 sentinel 26380
slave 6381 sentinel 26381
slave 6382 sentinel 26382
5.3.2.1 复制sentinel.conf

请将Redis安装目录下的sentinel.conf文件拷贝至cluster目录内。此配置文件位于Redis集群管理区域,并用于存储一些全局性的配置信息。

5.3.2.2 修改sentinel.conf

修改cluster/sentinel.conf配置文件。

5.3.2.2.1 sentinel monitor

此配置用于指定Sentinel监控的目标 master 并为其取名;此名称将在后续多个配置中被频繁使用;此外还与确定 master 在'客观下线状态'判定所需的法定 sentinel 数量相关;为此需要将此配置注释以避免后续设置时发生冲突;另外一项重要用途是与 sentinel 的 Leader 选举过程紧密相关:要求至少要有 max(quorum, sentinelNum/2+1) 个 sentinel 参与才能完成选举流程

5.3.2.2.2 sentinel auth-pass

当Redis主从集群中的主机配置了访问密码时,则需要将该属性配置为主机名和access password,并且这样做是为了便于sentinel用来监控master节点的状态。

5.3.2.3 新建sentinel26380.conf

在Redis安装目录下的cluster目录中创建位于该位置的sentinel26380.conf文件用于配置Sentinel工具,并在其内输入以下内容:

该属性负责指定Master节点的IP地址及端口,并为此集群中的Master节点分配一个标识符mymaster以便其他属性能够引用。
最后参数2表示quorum值有两个主要功能:一是只有当所有quorum数量的所有SENTINEL都确认当前MASTER节点发生故障时才会启动故障转移;二是另一个主要应用涉及SENTINEL节点选择领导者的机制,在这种情况下,在选举过程中必须确保参与选举的SENTINEL数量至少达到max(quorum, sentinelNum / 2 + 1)。

5.3.2.4 再复制两个conf文件

再次使用 sentinator-26380.conf 复制出两个配置文件:sendinator-26381.conf 和 sendinator-26382.conf;接着复制完成后进行配置;对 sendinator-26381.conf 进行修改。

修改sentinel26382.conf。

5.3.3 Redis高可用集群的启动

5.3.3.1 启动并关联Redis集群

首先要启动三台Redis,然后再通过slaveof关联它们。

5.3.3.2 启动Sentinel集群
5.3.3.2.1 启动命令

在/usr/local/bin目录中存在一个名为redis-sentinel的命令行工具用于驱动Sentinel服务的运行。值得注意的是,在此目录中发现了一个现象:Redis-Sentinel其实是Redis-Server的一个间接引用。这一现象背后的原因是什么呢?

探索Redis目录结构中src子目录下的redis-server与redis-sentinel两个Redis相关指令时会发现它们的规模大小完全相同 实际上在功能上这两个操作是一体化的

因为每次启动系统时所加载的配置文件不同,在启动生成监控器Sentinel时,请指定sentinel.conf配置文件。

5.3.3.2.2 两种启动方式

因为 redis-server 和 redis-sentinel 这两个术语实际上指的是同一命令, 因此可以通过这两个术语来启动Sentinel.

  • 第一种方法是调用Redis Sentinel命令: redis-sentinel sentinel26380.conf
    • 第二种方法是运行Redis Server并启用Sentinel功能: redis-server sentinel26380.conf --sentinel
5.3.3.2.3 启动三台Sentinel
5.3.3.3 查看Sentinel信息

运行中的Sentinel是一个特殊的Redis类型,并且它也可以通过客户端进行连接操作;此外,在调用info方法时能够获取到当前连接的相关信息。

5.3.3.4 查看sentinel配置文件

打开任意sentinel的配置文件,发现其配置内容中新增加了很多配置。

5.3.4 Sentinel优化配置

在公共的sentinel.conf文件中,还可以通过修改几个其他属性的值来完成对Sentinel的配置优化工作.

5.3.4.1 sentinel down-after-milliseconds

每个SENTINEL会定期使用ping命令向其所属的MASTER、_SLAVE及其他SENTINEL发送查询以确认其存活状态。如果一个SENTINEL在其属性所设定的时间段内未收到其他设备的响应信息,则可能会判断当前主机已发生故障。默认时间为30秒。

5.3.4.2 sentinel parallel-syncs

该属性用于指定故障转移过程中的操作,在老的 master 出现故障后(即当老的 master 出现故障时),新晋升的 master 可以同时允许多少个 slave 从其进行数据同步?其默认设置为1时意味着每个 slave 依次与新 master 进行数据同步。

5.3.4.3 sentinel failover-timeout

指定故障转移的超时时间,默认时间为3分钟。该超时时间的用途很多:

  • 在第一次故障转移失败的情况下,在同一个master上进行第二次故障转移尝试所需的时间是该failover-timeout值的两倍。
  • 当新Master完成晋升时(即new Master完成升级),slave节点被迫从旧Master转移到新Master以完成数据同步的任务时间阈值。
  • 取消当前正在进行的故障转换所需的时间阈值。
  • 当新Master完成升级后,在其上所有replicas节点的配置文件更新完毕的时间阈值。
5.3.4.4 sentinel deny-scripts-reconfig

是否可以通过sentinel命令设置指令来实现对notification-script和client-reconfig-script这两个脚本的动态更改?默认情况下是不允许这样做。如果允许对这两个脚本进行动态更改,则可能带来安全隐患。

5.3.4.5 动态修改配置

通过使用redis-cli工具连接至Sentinel服务器后,则可通过sentinel.set命令实现对配置参数的实时更新操作。例如以下操作能够直接修改相关设置:

参数 示例
quorum sentinel set mymaster quorum 2
down-after-milliseconds sentinel set mymaster down-after-milliseconds 50000
failover-timeout sentinel set mymaster failover-timeout 300000
parallel-syncs sentinel set mymaster parallel-syncs 3
notification-script sentinel set mymaster notification-script /var/redis/notify.sh
client-reconfig-script sentinel set mymaster client-reconfig-script /var/redis/reconfig.sh
auth-pass sentinel set mymaster auth-pass 111

5.4 哨兵机制原理

5.4.1 三个定时任务

为了确保Redis节点和其他Sentinel节点的稳定运行,Sentinel定期执行三个状态检查的任务.

5.4.1.1 info任务

每隔十秒左右的每一个Sentinel节点都会通过触发各个Redis集群成员的执行过程来通知所有其他节点。

5.4.1.2 心跳任务

每个Sentinel node每秒都会向所有的Redis node及其他Sentinel node发起一次 ping 请求,并在接收到所有响应后确认各node的运行状态。此任务可作为判断node在线状态的重要参考依据。

5.4.1.3 发布/订阅任务

每个Sentinel节点在启动时都会向所有Redis节点注册订阅_ sentinel :hello主题的信息,并在该主题信息的状态发生变化时立即通知所有注册者。
启动后,在每隔2秒左右会向每个Redis节点发布一条
sentinel :hello主题的信息。
这些发布的内容主要包含两个方面的内容:一是当前Sentinel对各个Redis节点在线状态的判断结果;二是当前Sentinate自身所掌握的相关信息。
一旦某个Sentinate接收到
sentinet:hello主题信息后,在接收并解析这些信息之后将主要完成以下三项工作:

  • 检测到新加入的Sentinel节点后,请保存相关信息并将其与现有节点进行连接。
  • 检测到有关Sentinel Leader选举的信息后,请触发Leader选举流程。
  • 整合其他Sentinel节点对于当前Redis服务器在线状态的分析结果,并将其作为Redis服务器进行客观性下线决策的基础依据。

5.4.2 Redis节点下线判断

对于每个Redis节点在线状态的监控是由Sentinel完成的。

5.4.2.1 主观下线

每个Sentinel节点每隔一秒都会向每一个Redis服务器发起心跳检测请求,在指定延迟时间后若未收到某Redis服务器的响应,则该Sentinel将该Redis服务器判定为处于"离线状态"。这种判定仅为当前Sentinel的一厢情愿,并非客观事实

5.4.2.2 客观下线

当某台 Sentiline 设备处于主观下线状态且其主从关系中对应的 master 节点时, 该设备会向所有其他 Sentiline 设备发送 '该设备所在的 master 节点已发生故障' 的命令, 以评估该 master 节点的在线状态。这些 Sentiline 设备在接收到该命令后将返回响应: 0 表示正常在线、1 表示已下线。当某台 Sentiline 设备检测到超过 quorum 数量的 master 下线报告后, 就会据此做出相应的 master 下线决策。

5.4.3 Sentinel Leader选举

当Sentinel节点对master做出客观下线判断后会由Sentinel Leader来完成后续的故障转移,即Sentinel集群中的节点也并非是对等节点,是存在Leader与Follower的。
Sentinel集群的Leader选举是通过Raft算法实现的。Raft算法比较复杂,后面会详细学习。这里仅简单介绍一下大致思路。
每个选举参与者都具有当选Leader的资格,当其完成了“客观下线”判断后,就会立即“毛遂自荐”推选自己做Leader,然后将自己的提案发送给所有参与者。其它参与者在收到提案后,只要自己手中的选票没有投出去,其就会立即通过该提案并将同意结果反馈给提案者,后续再过来的提案会由于该参与者没有了选票而被拒绝。当提案者收到了同意反馈数量大于等于max(quorum,sentinelNum/2+1)时,该提案者当选Leader。
说明:

  • 假设网络运行正常,在任何情况下,“客观下线”的判定一旦出现,“Sentinel Leader”的选举就会立即启动,并获得绝大多数参与者的认可后就成为新的Leader。
  • Sentinels的Leader选举将在第二次故障转移发生前立即启动。
  • 在故障转移结束时,“Sentinel将终止这种领导与跟随的关系。”

5.4.4 master选择算法

当发生故障转移时,在所有Redis的Slave节点中挑选出一个作为新的Master,并将利用以下指标来进行评估:负载均衡、性能稳定性以及可扩展性等关键参数。其中将利用以下指标来进行评估:负载均衡、性能稳定性以及可扩展性等关键参数。其中将利用以下指标来进行评估:负载均衡、性能稳定性以及可扩展性等关键参数。

  1. 去除所有存在主观下限的所有Redis节点,并排除其心跳探测功能未正常工作以及replica-priority值为零的情况。
  2. 在剩下的Redis nodes中筛选出replica-priority最低者构成候选列表;若候选列表仅包含一个node,则直接选取;否则继续下一步操作。
  3. 从优先级相同的所有候选nodes中挑选具有最大复制偏移量的那个node;若只有一个node,则直接选取。
  4. 最后,在复制偏移值相同的情况下挑选具有动态ID最小的那个node并返回。

5.4.5 故障转移过程

Sentinel Leader负责整个故障转移过程,经历了如上步骤:

  1. Sentinel Leader遵循 master 选择算法选出一个 slave 节点充当新的 master.
  2. Sentinl_leader 向 new_master 发出 slaveof none 指令.
  3. Sentinl_leader 发出 info replication 指令, 并准确地获取其动态 ID.
  4. Sentinl_leader 主动告知其余 Redis 节点接收到 new_master 的详细信息.
  5. Sentinl_leader 主动发出 slaveof 指令指示其成为 new_master 的子节点.
  6. Sentinl_leader 定期从每个 slave 节点中选取 parallel-syncs 个参与同步操作.
  7. 故障转移完毕

5.4.6 节点上线

不同的节点类型,其上线的方式也是不同的。

5.4.6.1 原Redis节点上线

无论是来自原下线的主服务器还是来自原下线的从服务器(slave),只要是来自原始 Redis 集群中的服务器在线上上线后,
启动 Redis 服务即可完成部署。
由于每个 Sentinel 都维护了一份原始监控的所有 Redis 节点列表,
每个 Sentinel 会定期检查这些 Redis 节点是否恢复。
如果发现它们已经恢复,则会命令它们从当前主服务器进行数据同步请求。
值得注意的是,在新主服务器升级时,
在线主服务器会被立即标记为从服务器(slave)状态,
之后系统才会定期检查该服务是否已恢复运行。

5.4.6.2 新Redis节点上线

如果需要在Redis集群中进行新增节点的插入操作,则该插入过程只能由人工完成。即,在插入过程开始前, 必须明确当前 master 节点是谁, 并且在新节点启动后执行 slaveof 命令以完成加入过程.

5.4.6.3 Sentinel节点上线

如果要添加的是Sentinel节点,则不论该Sentinel节点之前是否曾出现在过Sentinel集群中,在尝试添加时都必须先完成手工操作。这要求在执行操作前必须要明确当前的MasterIP地址。然后按照配置参数进行调整设置后就可以正常运行。

5.5 CAP定理

5.5.1 概念

CAP定理涉及在分布式系统环境中,在一致性(Consistency)、可用性(Availability)以及分区容错性(Partition tolerance)三者之间做出权衡和权衡。

  • 一致性(C):分布式系统的数据一致性特性的表现主要体现在各节点之间是否能够同步达成统一的数据状态。该特性要求,在分布式环境下进行的数据更新操作完成后,在所有节点中均能保持一致的数据状态。
  • 可用性(A):系统的高可用性意味着始终维持着可访问状态以满足每位用户的请求服务需求。不论用户何时发起请求,在有限的服务响应时间内都能够完成相应的处理工作以确保服务质量不受影响。
  • 区分容错性(P):面对网络分区故障情况时, 分布式系统依然具备确保提供既具有一致性又具备可用性的服务回应机制.该特性通过完善的容错机制, 在网络故障发生时依然能维持关键业务功能的正常运转.

5.5.2 定理

CAP定理阐述的是分布式系统的特性:当面对难以预测的网络环境变化时,在某些情况下必然会导致网络分区的存在。这意味着一个分布式系统无法同时满足一致性和高可用性这两个关键特性。即基于复制协议(Consistent Replication)实现的一致性存储方案(CP)或基于主从复制协议(Paxos)实现的一致性扩展方案(AP)。

5.5.3 BASE理论

BASE由Basically Available(基本可用)、Soft state(软状态)和Eventually consistent(最终一致性)三种常见术语构成,并代表了CAP定理中一致性与可用性权衡的结果。该理论源自对大规模互联网系统分布式架构实践经验的总结,在CAP定理指导下逐步演化而成。

5.5.3.1 基本可用

容错可用性是指一种设计原则,在这种原则下,在分布式系统中即使发生不可预见地发生故障的情况下,在不影响整体系统运行的同时也仅限于部分系统组件可能失效

5.5.3.2 软状态

软状态被定义为允许系统数据存在的中间状态,并认为这种状态下系统的整体可用性不受影响。即允许系统主机间进行数据同步的过程存在一定延时。软状态实际上是一种灰度过渡的状态。

5.5.3.3 最终一致性

最终一致性体现在系统中的任意一个数据副本,在完成同步操作后处于统一的状态。
因此,在本质上来说,
最终一致性需要通过机制确保系统的整体状态能够统一实现,
而无需依赖实时同步。

5.5.4 CAP的应用

下面将生产中常见到的一些中间件与服务器集群的CAP特性进行分析。

5.5.4.1 Zookeeper与CAP

Zookeeper采用了CP模式,在维持数据一致性的同时降低了服务可用性

5.5.4.2 Consul与CAP

Consul遵循的是CP模式,即保证了一致性,但牺牲了可用性。

5.5.4.3 Redis与CAP

Redis遵循的是AP模式,即保证了可用性,但牺牲了一致性。

5.5.4.4 Eureka与CAP

Eureka遵循的是AP模式,即保证了可用性,但牺牲了一致性。

5.5.4.5 Nacos与CAP

Nacos在做注册中心时,默认设置是AP的。它还提供CP模式的支持,并需经用户发起转换申请

5.6 Raft算法

5.6.1 基础

基于日志复制管理的机制, 该算法旨在确保集群节点间数据的一致性. 该算法通过协调 Leader 和 Follower 之间的操作来完成日志复制管理. 选出的 Leader 节点负责执行日志复制过程, 并以此确保所有节点的数据一致性.

5.6.2 角色、任期及角色转变

在Raft中,节点有三种角色:

  • Leader:仅限于处理客户端发起的事务性操作;除了上述功能外还具备读取事务数据的能力;并且支持执行完整的日志复制流程
  • Candidate:在其生命周期内参与选票生成过程;具备成为最终选出来的Leader资格;主要承担着组织者角色
  • Follower:能够完成客户端的数据读取任务;负责同步系统状态信息;当接收到其他候选人的投选指令后会立即响应并完成投选操作;一旦发现核心领导人出现故障则会立即转为Candidate身份并启动领导 contenders 选举流程

5.6.3 leader选举

通过Raft算法首先要实现集群中Leader的选举。

5.6.3.1 我要选举

当follower在其心跳超时范围内未能接收到来自leader的心跳时,则认为leader处于故障状态。此时其首先将本地term增加1。随后,follower将执行以下操作:

  • 当其他candidate收到投票请求时,则会将选票投给这个candidate
  • 从follower变为candidate
  • 如果之前没有进行过投票,则为自己投上一票
  • 向其他节点发出投选请求,并等待其回应
5.6.3.2 我要投票

follower在接收到投票请求后,其会根据以下情况来判断是否投票:

  • The term of the candidate must not be earlier than mine.
  • Within my current term, i have not yet submitted my vote.
  • When multiple candidates submit requests, i will follow a first-come-first-served protocol for voting.
5.6.3.3 等待响应

当一个Candidate在收到投票请求后会等待其它节点的响应。该响应结果可能出现以下三种情形:

  • 用户获得了超过半数选票并当选为新的领导角色。
    随后会向所有其他节点发送消息以通知大家我是新的领导者。
  • 每当用户接收到其他candidate发送的new leader notice时,
    经比较发现自己的term不小于新leader的term后,
    便决定转变为follower。
  • 当一段时间过去后,
    若未获得过半数选票且未接收到new leader notice,
    则会重新发起选举。
5.6.3.4 选举时机

通常情况下,在Leader发生故障时(即'挂掉'),多个Follower几乎同时察觉到了这一情况,并迅速转换为候选人角色以发起新的选举请求。然而,在某些情况下可能会出现多个候选人的票数相同而无法决出Leader的情况。为了避免这种问题的发生,《Raft算法》采用了一种基于随机化的选举超时机制来解决这一问题。该算法给每个Follower分配一个随机的选举超时时间值(范围在150-300毫秒之间)。只有当某个Follower的时间计数达到指定超时值后才会转换为候选人参与选举。如果某个Follower的超时值较低,则它将首先转换为候选人并尝试发起选举。一般来说,在完成多数票制下的最早完成者往往能够胜出并成为新的Leader

5.6.4 数据同步

当Leader选举完成时, 采用日志复制机制使得集群内的各个节点能够实现数据同步

5.6.4.1 状态机

Raft算法的一致性实现是基于日志复制的状态机的构建过程。该机制的核心特性在于:当不同服务器上的状态机处于相同的状态后,并接收完全一致的输入时,则必然会产生完全一致的输出结果。

5.6.4.2 处理流程

当leader接收到client的写操作请求后,大体会经历以下流程:

  • 当leader接收到client的一个write请求后,它会将数据与term打包成一个unit并随下一次心跳发送给所有follower,以收集各方对该unit的意见,同时在本地生成日志记录.
  • follower在获取到来自leader的unit后,会对比该unit与本地记录的最大值,如果不超过自己的最大值则接受该unit并将其中的数据打包成日志.
  • 当任意一方获得过半数量的同意响应时,会将当前的所有日志提交到自己的状态机并返回一个确认结果
  • 同时也会通知所有follower将当前的日志提交到各自的状态机并设置为committed状态
  • 在提交通知的同时,也会向客户端发送成功处理的消息
5.6.4.3 AP支持

Log由term index、log index以及command组成。为了确保系统的可用性需求得到满足,在各个节点中存储的日志信息可以并非完全一致。然而,在raft协议中,leader会持续向每个follower发送boxed命令。这表明raft算法并非强一致性协议。

5.6.5 脑裂

Raft集群存在脑裂问题。在多机房部署中,由于网络连接问题,很容易形成多个分区。而多分区的形成,很容易产生脑裂,从而导致数据不一致。
由于三机房部署的容灾能力最强,所以生产环境下,三机房部署是最为常见的。下面以三机房部署为例进行分析,根据机房断网情况,可以分为五种情况:

5.6.5.1 情况一–不确定

在这种情况中,在B 机房中存在无法感知 Leader 存在的状态时,则 B 机房中的主机将重新发起 Leader 的选举流程。由于 B 机房与 C 机房之间存在连接关系,在 C 机房中所有的 Follower 可以察觉到 A 机 room 中 Leader 的存在;然而由于 C 机 room 接收到来自 A 机器更大的 term 请求量,在此情况下 C 机器人的 Follower 就会放弃 A 机器人的 Leader 负责,并参与到新的 Leader 的选举流程中来。

当新 Leader 出现在 B 机器时,在 A 机器处将无法察觉到这一变化的发生;因此这会导致 brain split 的情况发生(或者系统会自动退出)。然而在实际情况中由于 A machine 处理 write 请求时未能获得足够的响应数量(即没有超过半数响应),因此 write 请求无法被成功处理或执行;而 B machine 处理 write 请求时却能够获得足够的响应数量(即超过半数响应),因此该请求能够被成功处理并完成相应的操作流程;这样就会导致 A machine 和 B、C machine 都会经历 brain split 的情况,并造成数据存储上的不一致问题。

5.6.5.2 情况二–形成脑裂

这种情形与情况一基本上是相同的。不同之处在于,在这种情况下必然会导致脑裂,并不论新Leader部署于B区还是C区的机房中。

5.6.5.3 情况三–无脑裂

A和C能够持续稳定地向外界发送信息;然而,在B中所有的主机都被设为选举模式后仍无法选出新的领导层。因为B中的所有主机都被设置为选举模式,并且导致没有任何服务能够正常运行

5.6.5.4 情况四–无脑裂

A、B、C均可以对外提供服务,不受影响。

5.6.5.5 情况五–无脑裂

A机房不具备处理写操作的能力,但仍可向外部提供读服务.

5.6.6 Leader宕机处理

5.6.6.1 请求到达前Leader挂了

在Client发起的"写操作"请求到达Leader节点之前, Leader就已经挂掉了。这是因为该"写操作"请求尚未抵达集群节点,因此在这段时间里集群内部并没有收到这一条"写操作"请求。由于这段时间内没有收到这一条"写操作",所以这条"写操作"对集群的数据一致性没有任何影响。之后, 集群将重新选举出一个新的Leader节点。因为Stale Leader节点没有成功地将处理响应返回给客户端,所以客户端将不得不重新发起这条"写操作"请求以解决当前的问题.

5.6.6.2 未开始同步数据前Leader挂了

当client提交一个写操作请求给Leader时,在Leader收到该请求并到达后发现Leader尚未开始向Followers推送数据便已失效(挂掉)。此时系统将执行集群重选举流程并任命新的Leader角色(Stale Leader)。新任Stale Leader将在完成自身重启后立即作为Follower重新加入集群环境,并同步其本地存储的数据以维持整体数据一致性。在此期间之前通过该客户节点接收的数据将被丢弃(由于Stale Leader未成功返回处理响应)。因此当Stale Leader未能有效响应客户端的确认反馈时客户端节点将自动发起重试机制重新提交该写操作请求。

5.6.6.3 同步完部分后Leader挂了

客户端向Leader发起写操作请求,在处理完数据后会将该数据广播给所有Follower节点。其中一些Follower接收到数据后发现Leader出现故障导致其失活。当这种情况发生时系统会自动触发新的Leader选举流程以保证集群的一致性。

  • 当Leader源自已成功接收数据的Follower时, 该Follower将之前接收的所有write操作记录下来, 并将其保存至本地状态机中, 然后通知所有其他follower. 只有当至少半数以上follower确认后, 该Follower才会发送commit指令给全体follower并立即返回给客户端.
    • 当Leader源自尚未完成全部数据接收的follower时, 那么原先已成功接收到的数据会被放弃. 因为客户端尚未收到该follower的确认信息, 所以客户端随后立即再次提交这条write请求给系统以重新处理.
5.6.6.4 commit通知发出后Leader挂了

Client发送了一个写操作请求给Leader,并且Leader在收到所有Followers提交的commit指令后立即向客户发起了响应。
由于Stale Leader已经成功接收并处理了客户的响应,并提前将提交通知发送给了系统中所有节点,
因此可以确定该写的请求已经被服务器端完成了处理流程。

5.6.7 Raft算法动画演示

在网络安全领域有一套专门用于展示Raft算法工作原理的动画视频

全部评论 (0)

还没有任何评论哟~