redis 4 - 高可用

一、简介

持久化技术保证了即使宕机也不会丢失数据。

但是单点故障会存在别的问题:

  • 服务器宕机后,整个服务完全不可用
  • 如果单台服务器的硬盘损坏,数据仍然会丢失

要避免单点故障,就需要使用集群,多加几台服务器。

Redis采用的是 「主从复制」 + 「读写分离」方案

二、如何实现数据同步?

第一次同步

如何确定谁是主服务器?

对服务器B执行命令,即可将B服务器变成A服务器的从服务器。

1
replicaof <服务器 A 的 IP 地址> <服务器 A 的 Redis 端口号>

同步流程

第一次同步流程

  1. 建立链接、协商同步;

执行replicaof命令后,从服务器会给主服务器发送psync命令,表示要进行数据同步。

psync命令包含两个参数,分别是主服务器的runID复制进度offset

  • runID,Redis服务器的身份标识(启动时生成),第一次同步时不知道主服务器的runID,设置为”?”
  • offset,表示复制的进度,第一次同步时,传-1

主服务器收到psync命令后,用FULLRESYNC作为响应返回给从服务器,会带上这两个参数,从服务器会记录下来。

FULLRESYNC响应的意思是要采用全量复制的方式,即主服务器会把所有的数据都同步给从服务器。

至此,做好了全量复制的准备。

  1. 主服务器同步数据给从服务器;

主服务器执行bgsave命令来生成RDB文件。

从服务器收到后,会清空当前数据然后载入RDB文件。载入完成后会给主服务器发送一个信号。

我们知道执行bgsave生成的RDB文件其实是主线程fork复制页表时那一刻的数据快照,在那之后新的写操作是被没有包含在RDB文件的。

所以主服务器会把收到载入完成信号之前收到的写操作命令,写入到一个缓冲区(replication buffer)中

  1. 主服务器发送新写操作命令给从服务器。

主服务器把缓冲区中记录的写操作命令发送给从服务器,从服务器执行这些操作。

完成了第一次同步工作。

后续同步

主从服务器完成第一次同步后,双方之间会维护一个TCP长连接。

后续主服务器通过这个连接持续把写操作命令传播给从服务器,保持主从服务器的数据一致。

但是如果服务器之间的网络连接断开,数据就不一致了。

如果后续网络连接恢复了,要怎么让数据重新一致呢?

Redis采用「增量复制」的方式

增量复制

主要有三个步骤:

  1. 从服务器发送psync命令
  2. 主服务器收到命令后,用CONTINUE命令告诉从服务器接下来采用增量复制的方式同步数据
  3. 主服务器把断线期间的写操作发送给从服务器,从服务器执行这些命令

这里有个问题,主服务器如何判断需要把哪些数据发送给从服务器?

答案是通过offset。

主服务器会有一个缓冲区(repc_backlog_buffer),保存着最近传播的写命令。

从服务器发送psync命令时,把自己的复制偏移量offset发送给主服务器,主服务器通过offset判断增量数据是否全部在缓冲区来决定执行哪种同步操作(全量OR增量)。

如果是增量同步,那么就会把增量数据写入到replication buffer缓冲区,然后通过命令传播的方式把写操作命令同步给从服务器。

为了避免在网络恢复时,主服务器频繁地使用全量同步的方式,我们应该调整一下repl_blocking_buffer缓冲区的大小。

尽可能大一点,使得在大多数情况下都使用增量同步的方式来同步数据。

三、哨兵

Redis主从架构中,如果主节点挂了,那么整个集群还是变得不可用了(无法写入)。

如果需要恢复服务,就需要人工介入,选择一个「从节点」切换成「主节点」,然后让其他从节点指向新的主节点,再通知连接Redis主节点的客户端,将其配置中的主节点IP地址更新为「新主节点」的IP地址

哨兵(Sentinel)就是用来做这些事情的。它会检测主节点的存活状态,在主节点故障后选举一个从节点切换为主节点。

哨兵主要就负责三件事情:监控、选主、通知

主要有三个问题:

  1. 哨兵节点如何监控节点?如何判断主节点是否故障了?
  2. 如何选择一个从节点为主节点
  3. 如何把新主节点的相关信息通知给从节点和客户端?

如何判断主节点真的故障了?

哨兵每隔一秒会给所有节点发送PING命令,节点收到后,会返回一个响应命令给哨兵,这样就可以判断它在正常运行。

如果节点没有在规定的时间内响应PING命令,哨兵会将它标记为「主观下线」。

这里「主观下线」不是真的下线。考虑到由于网络以及哨兵节点本身的原因可能造成误判,并且误判主节点下线带来的影响比较大(经常切换主节点),我们会采取手段尽量减少对主节点的误判。

这个减少误判的手段就是「部署哨兵集群」:哨兵在部署的时候不会只部署一个节点,而是用多个节点部署成哨兵集群,通过多个节点一起判断,避免单个哨兵节点误判主节点下线的情况。

当一个哨兵节点判断主节点「主观下线」后,就会向其他哨兵发起投票命令,其他哨兵收到后,会给出自己的判断。

当赞同票达到配置文件中的quorum配置项设定的值后,主节点会被标记为「客观下线」,即判断主节点真的故障了。

一般来说,哨兵的数量为奇数(2k + 1),quorum的值为k + 1

哨兵判断完主节点客观下线后,就开始在多个「从节点」中,选出一个从节点来做新主节点。

哪个哨兵来进行故障转移?

现在发现哨兵一般会部署成一个集群,那么就出现了一个新的问题,发现主节点下线后谁来执行后续的动作?

所以我们需要在哨兵集群中选举出一个leader,让leader来执行主从切换。

  1. 谁是「候选者」?

哪个哨兵节点判断主节点为「客观下线」,这个节点就是一个候选者。

  1. 候选者如何选举成为leader?

候选者向其他哨兵发送命令,表明希望成为leader,让其他哨兵投票。

每个哨兵都只有一次投票机会。

满足以下条件的候选者成为leader

  • 拿到半数以上的赞成票
  • 拿到的票数 >= quorum

如果没有候选者满足条件,则重新进行选举。

  1. 为什么哨兵节点至少要有三个?

如果哨兵集群中只有2个哨兵节点,一个哨兵想要成为leader,必须获得2票。如果某个哨兵挂掉了,哨兵集群就挂了。

如何选择新主节点?

那么多从节点,应该选择谁来作为新主节点呢?

如果随机选择,可能会选择到网络状态也不好的节点作为新主节点,可能不久后又需要进行主从故障迁移。

因此需要过滤掉网络状况不好的节点。

Redis会有一个配置项配置了主从节点连接超时时间,如果在这个超时时间内主从节点都没有通过网络联系上,就可以认为主从节点断连了。如果发生断连的次数超过十次,就说明这个节点的网络状况不好,不适合作为新主节点。

接下来会对所有从节点进行三轮优先级判断。选出优先级最高的从节点作为新主节点。

  1. 服务器可以手动配置从节点优先级。
  2. 复制进度最靠前的从节点。
  3. ID编号小的从节点。

新主节点选出后,通知相关方

  1. 通知被选中的从节点

哨兵leader向被选中的从节点发送SLAVEOF no one,然后每秒一次的频率向它发送INFO命令。

当该节点返回的角色信息从slave变成master之后,哨兵就知道它已经升级为主节点了。

  1. 让剩下的从节点指向新主节点

给剩下的从节点发送SALVEOF <ip> <port>命令,让他们成为新主节点的从节点。

  1. 通知客户端

哨兵节点会提供消息订阅频道,客户端可以进行订阅。

主从切换完成后,哨兵会向+switch-master频道发布新主节点的IP地址和端口的消息。

客户端收到消息后可以与新主节点进行通信。

哨兵节点还提供了别的频道,客户端可以监控到主从节点切换过程中发生的各个重要事件,了解主从切换进度。

  1. 将旧主节点变成从节点

哨兵会继续监视旧主节点,当旧主节点重新上线时,哨兵集群会向他发送SLAVEOF命令,让它成为新主节点的从节点。

哨兵集群是如何组成的?

Todo..


redis 4 - 高可用
https://yzaf.top/2023/redis/redis-4/
作者
why
发布于
2023年5月4日
许可协议