Redis高可用

Redis的高可用性是指在系统中使用Redis作为数据存储组件时,通过一系列的技术和架构设计,确保系统在面对硬件故障、网络问题或其他不可预测的情况下仍能够持续提供稳定的服务和数据可访问性。

在传统的单节点Redis部署中,如果该节点出现故障,整个系统的可用性会受到影响。为了提高Redis的可用性,通常采取以下一些方法:

  1. 主从复制(Master-Slave Replication): 在这种模式下,有一个主节点(Master)和多个从节点(Slaves)。主节点负责处理写操作,而从节点复制主节点的数据,处理读操作。如果主节点发生故障,可以将一个从节点升级为新的主节点,从而实现快速切换和恢复。
  2. 哨兵模式(Sentinel): 哨兵是一种监控和管理Redis集群的系统。它可以监测主节点和从节点的健康状况,当主节点不可用时,自动选择一个健康的从节点提升为新的主节点,以确保系统的持续可用性。
  3. 集群模式(Cluster): Redis集群模式将数据分散存储在多个节点上,每个节点负责管理部分数据。这种方式可以提供更高的可用性和横向扩展能力,但需要应用程序进行适当的分片和管理。
  4. 持久化和备份: Redis支持将数据持久化到磁盘,以便在系统重启后能够恢复数据。此外,定期创建备份也是一种保障数据的方法。
  5. 数据冗余: 将相同的数据存储在不同的节点上,以防止单一节点故障导致数据丢失。这可以通过主从复制、哨兵和集群模式来实现。
  6. 自动故障转移: 在主从复制和哨兵模式中,当主节点出现问题时,系统可以自动切换到一个健康的节点,从而避免服务中断。

总之,Redis的高可用性设计旨在减少单点故障的影响,保障系统的稳定运行。不同的场景可能选择不同的高可用方案,具体取决于系统的需求和规模。下面来谈谈前3种方式 .

主从复制

对于单点故障,最好的办法是将数据备份到其他服务器上,让这些服务器也可以对外提供服务,这样即使有一台服务器出现了故障,其他服务器依然可以继续提供服务,这就是Redis的主从复制 .

如何保证主从的数据一致性?

在Redis中,客户端复制向主服务器和从服务器发送读的指令 , 而只对主服务器发送写指令 . 当主服务器接收到写指令时, 再同步给从服务器 .具体可以分为以下几步 :

主要就是**全量复制(1-4)、基于长连接的命令传播(5)、增量复制(6)**三个步骤 . 以下是细分过程, :

  1. 配置主从服务器. replicaof <服务器 A 的 IP 地址> <服务器 A 的 Redis 端口号> #在服务器B上执行此条命令,让B服务器成为A的从

  2. 建立链接 . 此阶段 ,从服务器就会给主服务器发送 psync 命令,表示要进行数据同步, 主服务器用 FULLRESYNC(全量复制方式) 作为响应命令返回给对方。

  3. 主服务器同步数据给从服务器 . 主服务器会执行 bgsave(子进程,不阻塞) 命令来生成 RDB 文件,然后把文件发送给从服务器。从服务器收到 RDB 文件后,会先清空当前的数据,然后载入 RDB 文件。

  4. 主服务器发送新写操作命令给从服务器 .主服务器再将 replication buffer 缓冲区里所记录的写操作命令发送给从服务器,从服务器执行来自主服务器 replication buffer 缓冲区里发来的命令,这时主从服务器的数据就一致了。

    1-4 为主从服务器的第一次同步 .

  5. 命令传播(持续复制) . 主从服务器在完成第一次同步后,即从服务器的数据追赶上主服务器之后 ,双方之间就会维护一个 TCP 连接(长连接)。后续主服务器可以通过这个连接继续将写操作命令传播给从服务器(也就是重复第四步),然后从服务器执行该命令,使得与主服务器的数据库状态相同。

  6. 增量复制 .对于第5步而言,主从服务器基于长连接进行命令传播。但如果网络发生波动 ,连接断开 ,则依然会数据不一致 . 因此网络断开又恢复后,主从服务器会采用增量复制的方式继续同步,也就是只会把网络断开期间主服务器接收到的写操作命令,同步给从服务器。具体方法见下一小结 .

增量复制

增量复制的过程:

  1. 从服务器在恢复网络后,会发送 psync 命令给主服务器,此时的 psync 命令里的 offset 参数不是 -1;
  2. 主服务器收到该命令后,然后用 CONTINUE 响应命令告诉从服务器接下来采用增量复制的方式同步数据;
  3. 然后主服务将主从服务器断线期间,所执行的写命令发送给从服务器,然后从服务器执行这些命令。

如何确定增量内容?

在主服务器进行命令传播时,不仅会将写命令发送给从服务器,还会将写命令写入到 repl_backlog_buffer 缓冲区里,因此 这个缓冲区里会保存着最近传播的写命令, 但是这个缓冲区是一个环形缓冲区,太久之前的数据会被挤出 .

网络断开后,当从服务器重新连上主服务器时,从服务器会通过 psync 命令将自己的复制偏移量 slave_repl_offset 发送给主服务器,主服务器根据自己的 master_repl_offsetslave_repl_offset 之间的差距,然后来决定对从服务器执行哪种同步操作:

  • 如果判断出从服务器要读取的数据还在 repl_backlog_buffer 缓冲区里,那么主服务器将采用增量同步的方式 , 即将增量的数据写入到 replication buffer 缓冲区,即缓存将要传播给从服务器的命令。
  • 相反,如果判断出从服务器要读取的数据已经不存在 repl_backlog_buffer 缓冲区里,那么主服务器将采用全量同步的方式。

为了避免在网络恢复时,主服务器频繁地使用全量同步的方式,我们应该调整下 repl_backlog_buffer 缓冲区大小,尽可能的大一些,通过 repl-backlog-size 1mb配置

分摊主服务器的压力

在前面的分析中,我们可以知道主从服务器在第一次数据同步的过程中,主服务器会做两件耗时的操作:生成 RDB 文件和传输 RDB 文件。

主服务器是可以有多个从服务器的,如果从服务器数量非常多,而且都与主服务器进行全量同步的话,就会带来两个问题:

  • 由于是通过 bgsave 命令来生成 RDB 文件的,那么主服务器就会忙于使用 fork() 创建子进程,如果主服务器的内存数据非大,在执行 fork() 函数时是会阻塞主线程的,从而使得 Redis 无法正常处理请求;
  • 传输 RDB 文件会占用主服务器的网络带宽,会对主服务器响应命令请求产生影响。

那么在「从服务器」上执行replicaof <目标服务器的IP> 6379,使其作为目标服务器的从服务器,就可以形成类似树结构的服务器架构 . 如果说主服务器是最顶层节点,那么只需要对第二层节点生成发送 RDB文件 ,第二层再对第三层发送RDB文件 ,则有效的分摊了主服务器的压力 .

哨兵机制

哨兵(Sentinel)机制作用是实现主从节点故障转移。它会监测主节点是否存活,如果发现主节点挂了,它就会选举一个从节点切换为主节点,并且把新主节点的相关信息通知给从节点和客户端。

如何判断主节点故障?

哨兵通过周期性地(1s)向Redis所有的主从节点发送PING命令来监控节点的健康状态。如果哨兵在一定时间内未收到节点的响应,就会认为节点可能发生故障。

虽然服务器未响应,但可能是因为系统压力比较大或者网络发送了拥塞,导致在规定时间内没有响应,因此并不能直接判断服务器下线.

所有为了减少误判的情况,哨兵在部署的时候不会只部署一个节点,而是用多个节点部署成哨兵集群最少需要三台机器来部署哨兵集群),通过多个哨兵节点一起判断,就可以就可以避免单个哨兵因为自身网络状况不好,而误判主节点下线的情况。通常的做法是某个哨兵未收到响应时,会调动其他哨兵发起投票 .

如何故障转移?

在哨兵集群中,会通过投票的方式来决定由哪个哨兵进行故障转移(通常是发起投票的那个哨兵),并进行故障转移 , 主要分为以下几步 :

  1. 在已下线主节点(旧主节点)属下的所有「从节点」里面,挑选出一个从节点,并将其转换为主节点。
  2. 让已下线主节点属下的所有「从节点」修改复制目标,修改为复制「新主节点」;
  3. 将新主节点的 IP 地址和信息,通过「发布者/订阅者机制」通知给客户端;
  4. 继续监视旧主节点,当这个旧主节点重新上线时,将它设置为新主节点的从节点

集群

哨兵机制,可以实现主从库的自动切换。通过部署多个实例,就形成了一个哨兵集群。哨兵集群中的多个实例共同判断,可以降低对主库下线的误判率。

一旦多个实例组成了哨兵集群,即使有哨兵实例出现故障挂掉了,其他哨兵还能继续协作完成主从库切换的工作,包括判定主库是不是处于下线状态,选择新主库,以及通知从库和客户端。

哨兵集群通信(发布/订阅 机制)

在主从集群中,主库上有一个名为“__sentinel__:hello”的频道,不同哨兵就是通过它来相互发现,实现互相通信的。

即哨兵 1 把自己的 IP(172.16.19.3)和端口(26579)发布到“__sentinel__:hello”频道上,哨兵 2 和 3 订阅了该频道。那么此时,哨兵 2 和 3 就可以从这个频道直接获取哨兵 1 的 IP 地址和端口号。然后,哨兵 2、3 可以和哨兵 1 建立网络连接。通过这个方式,哨兵 2 和 3 也可以建立网络连接,这样一来,哨兵集群就形成了。它们相互间可以通过网络连接进行通信,比如说对主库有没有下线这件事儿进行判断和协商。