5.redis主从集群

254 阅读21分钟

为了避免 Redis 的单点故障问题,我们可以搭建一个 Redis 集群,将数据备份到集群中的其它节点。若一个 Redis 节点宕机,则由集群中的其它节点顶上。

5.1主从集群搭建

Redis 的主从集群是一个“一主多从”的读写分离集群。集群中的 Master 节点负责处理客户端的读写请求,而 Slave 节点仅能处理客户端的读请求。之所以要将集群搭建为读写分离模式,主要原因是对于数据库集群,压力大多数来自于读操作请求。所以,只有一个节点负责处理写操作请求即可。

5.1.1 伪集群搭建与配置

在采用单线程 IO 模型时,为了提高处理器的利用率,一般在一个主机中安装多台 Redis, 构建一个 Redis 主从伪集群。下面要搭建的读写分离伪集群包含一个 Master 与两个 Slave。它们的端口号分别是:6380、 6381、6382。

(1) 复制 redis.conf

在 redis 安装目录中 mkdir 一个目录,名称随意。这里命名为 cluster。然后将 redis.conf 文件复制到 cluster 目录中。该文件后面会被其它配置文件包含,所以该文件中需要设置每个 Redis 节点相同的公共的属性。

cp redis.conf cluster

(2) 新建 redis6380.conf

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

include redis.conf
pidfile /var/run/redis_6380.pid
port 6380
dbfilename dump6380.rdb
appendfilename appendonly6380.aof
replica-priority 90

# logfile access6380.log

(3) 再复制出两个 conf 文件

使用 redis6380.conf 复制出两个 conf 文件:redis6381.conf 与 redis6382.conf。修改其中的内容。把6380修改成相应的。 replica-priority每个文件减10

(4) 启动三台 Redis image.png

(5) 设置主从关系

再打开三个会话框,用客户端连接三台 Redis。然后通过 slaveof 命令,指定 6380 的 Redis 为 Master。 image.png

(6)查看状态信息

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

5.1.2 分级管理

若 Redis 主从集群中的 Slave 较多时,它们的数据同步过程会对 Master 形成较大的性能压力。此时可以对这些 Slave 进行分级管理。 image.png 让低级别 Slave 指定其 slaveof 的主机为其上一级 Slave 即可 image.png

5.1.3 容灾冷处理

若 Master 宕机怎么办呢?有两种处理方式,一种是通过手工角色调整,使 Slave 晋升为 Master 的冷处理;一种是使用哨兵模式,实现 Redis 集群的高可用 HA,即热处理。

无论 Master 是否宕机,Slave 都可通过 slaveof no one 将自己由 Slave 晋升为 Master。如果其原本就有下一级的 Slave,就直接变为了这些 Slave 真正的 Master 。而原来的 Master 也会失去这个原来的 Slave。

5.2 主从复制原理

5.2.1 主从复制过程

当一个 Redis 节点(slave 节点)接收到类似 slaveof 127.0.0.1 6380 的指令后直至其可以从 master 持续复制数据,大体经历了如下几个过程: image.png image.png (1) 保存 master 地址

slave 接收到 slaveof 指令后,将新的 master 的地址保存。

(2) 建立连接

slave 中维护着一个定时任务,该任务会尝试与 master 建立 socket 连接。如果连接无法建立,则其会不断定时重试,直到连接成功或接收到 slaveof no one 指令。

(3) slave 发送 ping 命令

连接建立成功,slave 发送 ping 进行首次通信。如果没有收到 master 的回复,则主动断开连接,下次的定时任务会重新尝试连接。

(4) 对 slave 身份验证

如果 master 收到了 slave 的 ping 命令,并不会立即对其进行回复,而是会先进行身份验证。验证失败则发送消息拒绝连接;成功则向 slave 发送连接成功响应。

(5) master 持久化

首次通信成功后,slave 会向 master 发送数据同步请求。当 master 接收到请求后,会 fork 出一个子进程,让子进程以异步方式立即进行持久化。

(6) 数据发送

持久化完毕后 master 会再 fork 出一个子进程,以异步方式将数据发送给 slave。slave 会将接收到的数据不断写入到本地的持久化文件中。 在 slave 数据同步过程中,master 的主进程仍在不断地接受着客户端的写操作,且不仅将新的数据写入到了 master 内存,同时也写入到了同步缓存。当 master 的持久化文件中的数据发送完毕后,master 会再将同步缓存中新的数据发送给 slave,由 slave 将其写入到本地持久化文件中。数据同步完成。

(7) slave 恢复内存数据

当 slave 与 master 的数据同步完成后,slave 就会读取本地的持久化文件,将其恢复到本地内存,然后就可以对外提供读服务了。

(8) 持续增量复制

在 slave 对外提供服务过程中,master 会持续不断的将新的数据以增量方式发送给 slave,以保证主从数据的一致性。

5.2.2 数据同步演变过程

(1) sync 同步

Redis 2.8 之前,首次通信成功后,slave 会向 master 发送 sync 数据同步请求。然后 master 就会将其所有数据全部发送给 slave,由 slave 保存到其本地的持久化文件中。这个过程称为全量复制。

存在一个问题:在全量复制过程中可能会出现由于网络抖动而导致复制过程中断。当网络恢复后,slave 与 master 重新连接成功,此时 slave 会重新发送 sync 请求,从头开始全量复制。

由于全量复制非常耗时,所以出现网络抖动的概率很高。而中断后的从头开始不仅需要消耗大量的系统资源、网络带宽,而且可能会出现长时间无法完成全量复制的情况。

(2) psync 同步

Redis 2.8 之后,全量复制采用了 psync(Partial Sync,不完全同步)同步策略。当全量复制过程出现由于网络抖动而导致复制过程中断时,当重新连接成功后,复制过程可以 断点续传。即从断开位置开始继续复制。这就大大提升了性能。

为了实现 psync,整个系统做了三个大的变化:

  • 复制偏移量:系统为每个要传送数据进行了编号,从 0 开始,每个字节一个编号。该编号称为复制偏移量。参与复制的主从节点都会维护该复制偏移量。 image.png master 每发送过一个字节数据后就会进行累计。统计信息 master_repl_offset。同时,slave 会定时向 master 上报其自身已完成的复制偏移量,所以 master 也会保存 slave 的复制偏移量 offset。
  • 主节点复制 ID:master 启动后会动态生成一个长度为 40 位的 16 进制字符串作为当前 master 的复 制ID,该 ID 是在进行数据同步时 slave 识别 master 使用的。 master_replid 属性。
  • 复制积压缓冲区:当 master 有连接的 slave 时,在 master 中就会创建并维护一个队列 backlog,默认大小 1MB,该队列称为复制积压缓冲区。master 接收到了写操作数据不仅会写入到 master 主存,写入到 master 中为每个 slave 配置的发送缓存,而且还会写入到复制积压缓冲区。其作用就是用于保存最近操作的数据,以备“断点续传”时做数据补偿,防止数据丢失。
  • psync 同步过程image.png psync 是一个由 slave 提交的命令,其格式为 psync <master_replied><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 自身情况会给出不同的响应结果。响应结果有三种可能:

  1. FULLRESYNC :告知 slave 当前 master 的动态 ID 及可以开始全量复制了,这里的 repl_offset 一般为 0
  2. CONTINUE:告知 slave 可以按照你提交的 repl_offset 后面位置开始“续传”了
  3. ERR:告知 slave,当前 master 的版本低于2.8,不支持 psyn,你可以开始全量复制了
  • psync 存在的问题
    • 在 psync 数据同步过程中,若 slave 重启,在 slave 内存中保存的 master 的动态 ID 与续传 offset 都会消失,“断点续传”将无法进行,从而只能进行全量复制,导致资源浪费。
    • 在 psync 数据同步过程中,master 宕机后 slave 会发生“易主”,从而导致 slave 需要从新 master 进行全量复制,形成资源浪费。

(3) psync 同步的改进

“同源增量同步”策略

  • 解决 slave 重启问题:改进后的 psync 将 master 的动态 ID 写入 slave 的持久化文件。
  • 解决 slave 易主问题:本质原因是新 master 不认识 slave 提交的 psync 请求中“原 master 的动态 ID”。新 master 中恰好保存的有“原 master 的动态 ID”。由于改进后的 psync 中每个 slave 都在本地保存了当前 master 的动态 ID,所以当 slave 晋升为新的 master 后,其本地仍保存有之前 master 的动态 ID。

(4) 无盘操作

Redis 6.0 对同步过程又进行了改进,提出了“无盘全量同步”与“无盘加载”策略,避免了耗时的 IO 操作。

  • 无盘全量同步:master 的主进程 fork 出的子进程直接将内存中的数据发送给 slave,无需经过磁盘。
  • 无盘加载:slave 在接收到 master 发送来的数据后不需要将其写入到磁盘文件,而是直接写入到内存,这样 slave 就可快速完成数据恢复。

(5) 共享复制积压缓冲区

Redis 7.0 对复制积压缓冲区进行了改进,让各个 slave 的发送缓冲区共享复制积压缓冲区。这使得复制积压缓冲区的作用,除了可以保障数据的安全性外,还作为所有 slave 的发送缓冲区,充分利用了复制积压缓冲区。

5.3 哨兵机制实现

对于 Master 宕机后的冷处理方式是无法实现高可用的。Redis 从 2.6 开始提供了高可用的解决方案—— Sentinel 哨兵机制。在集群中再引入一个节点,该节点充当哨兵,用于监视 Master 的运行状态,在 Master 宕机后自动指定一个 Slave 作为新的 Master。 整个过程无需人工参与,完全由哨兵自动完成。

不过,此时哨兵又成为了一个单点故障点:若哨兵发生宕机,整个集群将瘫痪。所以为了解决单点问题,又要为 Sentinel 创建一个集群。一个哨兵的宕机,将不会影响到 Redis 集群的运行。

那么这些哨兵是如何工作的呢?如何知道其监视的 Master 状态的呢? 每个哨兵都会定时会向 Master 发送心跳,如果 Master 在有效时间内向它们都进行了响应,说明 Master 是“活着的”。如果有 quorum 个哨兵没收到响应,就认为 Master 已经宕机,会有一个哨兵做 Failover 故障转移。即将原来的某一个 Slave 晋升为 Master。

5.3.1 Redis 高可用集群搭建

在“不差钱”的情况下,可以让哨兵占用独立的主机。下面要搭建一个“一主二从三哨兵”的高可用伪集群,即这些角色全部安装运行在一台主机上。“一主二从”使用前面的主从集群,下面仅搭建一个哨兵伪集群。
image.png

(1) 复制 sentinel.conf

将 Redis 安装目录中的 sentinel.conf 文件复制到 cluster 目录中。该配置文件中用于存放一些哨兵集群中的一些公共配置。

(2) 修改 sentinel.conf

sentinel monitor image.png
该配置用于指定 Sentinel 要监控的 master 是谁,并为 master 起名。该名字在后面很多配置中都会使用。同时指定 Sentinel 集群中决定该master“客观下线状态”判断的法定哨兵数量quorum。quorum的另一个用途与哨兵的 Leader 选举有关。要求中至少要有 max(quorum, sentinelNum/2+1)个哨兵参与, 选举才能进行。

image.png
这里将该配置注释掉,因为要在后面的其它配置文件中设置,如果不注释就会出现配置冲突。

sentinel auth-pass:如果 Redis 主从集群中的主机设置了访问密码,那么该属性就需要指定 master 的主机名与访问密码。方便哨兵监控 master。

(3) 新建 sentinel26380.conf

在 cluster 目录中新建 sentinel26380.conf 文件作为哨兵的配置文件,并在其中键入如下内容:

include sentinel.conf
pidfile /var/run/sentinel_26380.pid
port 26380
sentinel monitor mymaster 192.168.192.102 6380 2

# logfile access26380.log

sentinel monitor 属性用于指定当前监控的 master 的 IP 与 Port,同时为集群中 master 指定一个名称 mymaster,以方便其它属性使用。 最后的 2 是参数 quorum 的值。

(4) 再复制两个 conf 文件

再使用sentinel26380.conf 复制出两个conf文件:sentinel26381.conf与sentinel26382.conf。然后修改其中的内容(编号换成自己的)。

5.3.2 Redis 高可用集群的启动

(1) 启动并关联 Redis 集群

首先要启动三台 Redis,然后再通过 slaveof 关联它们。就是刚刚启动的主从集群。

(2) 启动哨兵集群

启动方式两种:

  1. 使用 redis-sentinel 命令:redis-sentinel sentinel26380.conf
  2. 使用 redis-server 命令:redis-server sentinel26380.conf --sentine

两个命令本质上是一个命令,主要是通过配置文件控制不同进程 image.png

(3) 查看 Sentinel 信息

运行中的 Sentinel 就是一个特殊 Redis,其也可以通过客户端连接,然后通过 info sentinel 来查看当前连接的 Sentinel 的信息 image.png

(4) 查看 sentinel 配置文件 打开任意 sentinel 的配置文件,发现其配置内容中新增加了很多配置 image.png

5.3.3 Sentinel 优化配置

在公共的 sentinel.conf 文件中,还可以通过修改一些其它属性的值来达到对哨兵的配置优化。

(1) sentinel down-after-milliseconds

哨兵定期发送ping命令判断master、slave以及其他哨兵是否存活。默认为30秒

(2) sentinel parallel-syncs

老的 master 出现问题,新的 master 刚晋升后,允许多少个 slave 同时从新 master 进行数据同步。默认值为 1 表示所有 slave 逐个从新 master 进行数据同步

(3) sentinel deny-scripts-reconfig

指定是否可以通过命令 sentinel set 动态修改 notification-script 与 client-reconfig-script 两个脚本。默认是不能的。这两个脚本如果允许动态修改,可能会引发安全问题。

(4) 动态修改配置

通过 redis-cli 连接上哨兵后,通过 sentinel set 命令可动态修改配置信息。

image.png

5.4哨兵机制原理

5.4.1 三个定时任务

哨兵维护着三个定时任务以监测 Redis 节点及其它哨兵节点的状态。

(1) info 任务

每个哨兵节点每 10 秒向 Redis 集群中的每个节点发送 info 命令,以获得最新的 Redis 拓扑结构。

(2) 心跳任务

每个哨兵节点每1秒向所有Redis节点及其它哨兵节点发送一条ping命令, 检测这些节点的存活状态。该任务是判断节点在线状态的重要依据。

(3) 发布/订阅任务

每个哨兵节点在启动时都会向所有 Redis 节点订阅_ _sentinel_ _:hello 主题的信息,当 Redis 节点中该主题的信息发生了变化,就会立即通知到所有订阅者。

启动后,每个哨兵节点每 2 秒向每个 Redis 节点发布一条_ _sentinel_ _:hello 主题的信息,该信息是当前哨兵对每个 Redis 节点在线状态的判断结果及当前哨兵节点信息。

当哨兵节点接收到_ _sentinel_ _:hello主题信息后,就会读取并解析这些信息,主要完成以下三项工作:

  • 如果发现有新的哨兵节点加入,则记录其信息,并与其建立连接。
  • 如果发现有 Sentinel Leader 选举的选票信息,则执行 Leader 选举过程。
  • 汇总其它哨兵节点对当前 Redis 节点在线状态的判断结果,作为 Redis 节点客观下线的判断依据。
5.4.2 Redis 节点下线判断

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

(1) 主观下线

每个哨兵节点每秒向每个 Redis 节点发送 ping 心跳检测,如果哨兵在 down-after-milliseconds 时间内没有收到某 Redis 节点的回复,则对该节点做出“下线状态”的判断。这个判断仅仅是当前 Sentinel 节点的“一家之言”,所以称为主观下线。

(2) 客观下线

当哨兵主观下线的节点是 master 时,该哨兵节点会向每个其它哨兵节点发送 sentinel is-master-down-by-addr 命令,以询问其对 master 在线状态的判断结果。这些哨兵节点在收到命令后会向这个发问 哨兵节点响应 0(在线)或 1(下线)。当收到超过 quorum 个下线判断后,就会对 master 做出客观下线判断。

5.4.3 Sentinel Leader 选举

当哨兵节点对 master 做出客观下线判断后会由 Sentinel Leader 来完成后续的故障转移,即哨兵集群中的节点也并非是对等节点,是存在 Leader 与 Follower 的。

哨兵集群的 Leader 选举是通过 Raft 算法实现的。这里仅简单介绍一下大致思路。

每个选举参与者都具有当选 Leader 的资格,当其完成了“客观下线”判断后,就会立即“毛遂自荐”推选自己做 Leader,然后将自己的提案发送给所有参与者。其它参与者收到提案后,只要自己手中的选票没有投出去,其就会立即通过该提案并将同意结果反馈给提案者,后续再过来的提案会由于该参与者没有了选票而被拒绝。当提案者收到了同意反馈数 量大于等于 max(quorum,sentinelNum/2+1)时,该提案者当选 Leader。

说明:

  • 在网络没问题的前提下,基本就是谁先做出了“客观下线”判断,谁就会首先发起 Sentinel Leader 的选举,谁就会得到大多数参与者的支持,谁就会当选 Leader。
  • Sentinel Leader 选举会在每次故障转移发生之前进行。
  • 故障转移结束后 Leader 不再存在。
5.4.4 master 选择算法

在进行故障转移时,Sentinel Leader 需要从所有 Slave 节点中选择出新的 Master。 其选择算法为:

  1. 过滤掉所有主观下线的,或心跳没有响应哨兵的,或 replica-priority 值为 0 的 Redis 节点
  2. 在剩余 Redis 节点中选择出 replica-priority 最小的的节点列表。如果只有一个节点,则直接返回,否则,继续
  3. 从优先级相同的节点列表中选择复制偏移量最大的节点。如果只有一个节点,则直接返回,否则,继续
  4. 从复制偏移值量相同的节点列表中选择动态 ID 最小的节点返回
5.4.5 故障转移过程

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

  1. Sentinel Leader 根据 master 选择算法选择出一个 slave 节点作为新的 master
  2. Sentinel Leader 向新 master 节点发送 slaveof no one 指令,使其晋升为 master
  3. Sentinel Leader 向新 master 发送 info replication 指令,获取到 master 的动态 ID
  4. Sentinel Leader 向其余 Redis 节点发送消息,以告知它们新 master 的动态 ID
  5. Sentinel Leader 向其余 Redis 节点发送 slaveof 指令,使它们成为新 master 的 slave
  6. Sentinel Leader 从所有 slave 节点中每次选择出 parallel-syncs 个 slave 从新 master 同步数 据,直至所有 slave 全部同步完毕
  7. 故障转移完毕
5.4.6 节点上线

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

(1) 原 Redis 节点上线

无论是原下线的 master 节点还是原下线的 slave 节点,只要是原 Redis 集群中的节点上线,只需启动 Redis 即可。因为每个哨兵都保存有原来其监控的所有 Redis 节点列表,哨兵会定时查看这些 Redis 节点是否恢复。如果查看到其已经恢复,则会命其从当前 master 进行数据同步。 如果是原 master 上线,在新 master 晋升后 Sentinel Leader 会立即先将原 master 节点更新为 slave,然后才会定时查看其是否恢复。

(2) 新 Redis 节点上线

上线操作只能手工完成。即添加者在添加之前必须知道当前 master 是谁,然后在新节点启动后运行 slaveof 命令加入集群。

(3) Sentinel 节点上线

如果要添加的是哨兵节点,无论其是否曾经出现在 Sentinel 集群中,都要手工完成。即添加者在添加之前必须知道当前 master 是谁,然后在配置文件中修改 sentinel monitor 属性,指定要监控的 master。然后启动哨兵。

5.5 CAP 定理

5.5.1 概念

CAP 定理指的是在一个分布式系统中,一致性 Consistency、可用性 Availability、分区容错性 Partition tolerance,三者不可兼得。

  • 一致性:分布式系统中多个主机之间是否能够保持数据一致的特性。当系统数据发生更新操作后,各个主机中的数据仍然处于一致的状态。
  • 可用性:系统提供的服务必须一直处于可用的状态,即对于用户的每一个请求,系统总是可以在有限的时间内对用户做出响应。
  • 分区容错性:分布式系统在遇到任何网络分区故障时,仍能够保证对外提供满足一致性和可用性的服务。
5.5.2 定理

对于分布式系统,网络环境相对是不可控的,出现网络分区是不可避免的,因此系统必须具备分区容错性。但系统不能同时保证一致性与可用性。即要么 CP, 要么 AP。

5.5.3 BASE 理论

BASE 是 Basically Available(基本可用)、Soft state(软状态)和 Eventually consistent(最终一致性)三个短语的简写,BASE 是对 CAP 中一致性和可用性权衡的结果,其来源于对大规模互联网系统分布式实践的结论,是基于 CAP 定理逐步演化而来的。

BASE 理论的核心思想是:即使无法做到强一致性,但每个系统都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性。

(1) 基本可用

指分布式系统在出现不可预知故障时,允许损失部分可用性

(2) 软状态

指允许系统数据存在的中间状态,并认为该中间状态的存在不会影响系统的整体可用性,即允许系统主机间进行数据同步的过程存在一定延时。软状态,其实就是一种灰度状态,过渡状态。

(3) 最终一致性

强调系统中所有的数据副本,在经过一段时间的同步后,最终能够达到一个一致的状态。

5.5.4 CAP 的应用

(1) Zookeeper 与 CAP

Zookeeper 遵循 CP 模式,即保证了一致性,但牺牲了可用性。当 Leader 节点中的数据发生变化后,在 Follower 还没有同步完成之前,整个 Zookeeper 集群不对外提供服务。如果此时有客户端来访问数据,则客户端会因访问超时而发生重试。不过,由于 Leader 的选举非常快,所以这种重试对于用户来说几乎是感知不到的。

(2) Redis 与 CAP

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