redis 4 - 高可用
一、简介
持久化技术保证了即使宕机也不会丢失数据。
但是单点故障会存在别的问题:
- 服务器宕机后,整个服务完全不可用
- 如果单台服务器的硬盘损坏,数据仍然会丢失
要避免单点故障,就需要使用集群,多加几台服务器。
Redis采用的是 「主从复制」 + 「读写分离」方案
二、如何实现数据同步?
第一次同步
如何确定谁是主服务器?
对服务器B执行命令,即可将B服务器变成A服务器的从服务器。
1 |
|
同步流程
- 建立链接、协商同步;
执行replicaof
命令后,从服务器会给主服务器发送psync
命令,表示要进行数据同步。
psync
命令包含两个参数,分别是主服务器的runID和复制进度offset
- runID,Redis服务器的身份标识(启动时生成),第一次同步时不知道主服务器的runID,设置为”?”
- offset,表示复制的进度,第一次同步时,传-1
主服务器收到psync
命令后,用FULLRESYNC
作为响应返回给从服务器,会带上这两个参数,从服务器会记录下来。
FULLRESYNC
响应的意思是要采用全量复制的方式,即主服务器会把所有的数据都同步给从服务器。
至此,做好了全量复制的准备。
- 主服务器同步数据给从服务器;
主服务器执行bgsave
命令来生成RDB文件。
从服务器收到后,会清空当前数据然后载入RDB文件。载入完成后会给主服务器发送一个信号。
我们知道执行bgsave
生成的RDB文件其实是主线程fork复制页表时那一刻的数据快照,在那之后新的写操作是被没有包含在RDB文件的。
所以主服务器会把收到载入完成信号之前收到的写操作命令,写入到一个缓冲区(replication buffer
)中
- 主服务器发送新写操作命令给从服务器。
主服务器把缓冲区中记录的写操作命令发送给从服务器,从服务器执行这些操作。
完成了第一次同步工作。
后续同步
主从服务器完成第一次同步后,双方之间会维护一个TCP长连接。
后续主服务器通过这个连接持续把写操作命令传播给从服务器,保持主从服务器的数据一致。
但是如果服务器之间的网络连接断开,数据就不一致了。
如果后续网络连接恢复了,要怎么让数据重新一致呢?
Redis采用「增量复制」的方式
主要有三个步骤:
- 从服务器发送
psync
命令 - 主服务器收到命令后,用
CONTINUE
命令告诉从服务器接下来采用增量复制的方式同步数据 - 主服务器把断线期间的写操作发送给从服务器,从服务器执行这些命令
这里有个问题,主服务器如何判断需要把哪些数据发送给从服务器?
答案是通过offset。
主服务器会有一个缓冲区(repc_backlog_buffer
),保存着最近传播的写命令。
从服务器发送psync
命令时,把自己的复制偏移量offset发送给主服务器,主服务器通过offset判断增量数据是否全部在缓冲区来决定执行哪种同步操作(全量OR增量)。
如果是增量同步,那么就会把增量数据写入到replication buffer
缓冲区,然后通过命令传播的方式把写操作命令同步给从服务器。
为了避免在网络恢复时,主服务器频繁地使用全量同步的方式,我们应该调整一下repl_blocking_buffer
缓冲区的大小。
尽可能大一点,使得在大多数情况下都使用增量同步的方式来同步数据。
三、哨兵
Redis主从架构中,如果主节点挂了,那么整个集群还是变得不可用了(无法写入)。
如果需要恢复服务,就需要人工介入,选择一个「从节点」切换成「主节点」,然后让其他从节点指向新的主节点,再通知连接Redis主节点的客户端,将其配置中的主节点IP地址更新为「新主节点」的IP地址
哨兵(Sentinel
)就是用来做这些事情的。它会检测主节点的存活状态,在主节点故障后选举一个从节点切换为主节点。
哨兵主要就负责三件事情:监控、选主、通知
主要有三个问题:
- 哨兵节点如何监控节点?如何判断主节点是否故障了?
- 如何选择一个从节点为主节点
- 如何把新主节点的相关信息通知给从节点和客户端?
如何判断主节点真的故障了?
哨兵每隔一秒会给所有节点发送PING
命令,节点收到后,会返回一个响应命令给哨兵,这样就可以判断它在正常运行。
如果节点没有在规定的时间内响应PING
命令,哨兵会将它标记为「主观下线」。
这里「主观下线」不是真的下线。考虑到由于网络以及哨兵节点本身的原因可能造成误判,并且误判主节点下线带来的影响比较大(经常切换主节点),我们会采取手段尽量减少对主节点的误判。
这个减少误判的手段就是「部署哨兵集群」:哨兵在部署的时候不会只部署一个节点,而是用多个节点部署成哨兵集群,通过多个节点一起判断,避免单个哨兵节点误判主节点下线的情况。
当一个哨兵节点判断主节点「主观下线」后,就会向其他哨兵发起投票命令,其他哨兵收到后,会给出自己的判断。
当赞同票达到配置文件中的quorum
配置项设定的值后,主节点会被标记为「客观下线」,即判断主节点真的故障了。
一般来说,哨兵的数量为奇数(2k + 1),
quorum
的值为k + 1
哨兵判断完主节点客观下线后,就开始在多个「从节点」中,选出一个从节点来做新主节点。
哪个哨兵来进行故障转移?
现在发现哨兵一般会部署成一个集群,那么就出现了一个新的问题,发现主节点下线后谁来执行后续的动作?
所以我们需要在哨兵集群中选举出一个leader,让leader来执行主从切换。
- 谁是「候选者」?
哪个哨兵节点判断主节点为「客观下线」,这个节点就是一个候选者。
- 候选者如何选举成为leader?
候选者向其他哨兵发送命令,表明希望成为leader,让其他哨兵投票。
每个哨兵都只有一次投票机会。
满足以下条件的候选者成为leader
- 拿到半数以上的赞成票
- 拿到的票数 >=
quorum
如果没有候选者满足条件,则重新进行选举。
- 为什么哨兵节点至少要有三个?
如果哨兵集群中只有2个哨兵节点,一个哨兵想要成为leader,必须获得2票。如果某个哨兵挂掉了,哨兵集群就挂了。
如何选择新主节点?
那么多从节点,应该选择谁来作为新主节点呢?
如果随机选择,可能会选择到网络状态也不好的节点作为新主节点,可能不久后又需要进行主从故障迁移。
因此需要过滤掉网络状况不好的节点。
Redis会有一个配置项配置了主从节点连接超时时间,如果在这个超时时间内主从节点都没有通过网络联系上,就可以认为主从节点断连了。如果发生断连的次数超过十次,就说明这个节点的网络状况不好,不适合作为新主节点。
接下来会对所有从节点进行三轮优先级判断。选出优先级最高的从节点作为新主节点。
- 服务器可以手动配置从节点优先级。
- 复制进度最靠前的从节点。
- ID编号小的从节点。
新主节点选出后,通知相关方
- 通知被选中的从节点
哨兵leader向被选中的从节点发送SLAVEOF no one
,然后每秒一次的频率向它发送INFO
命令。
当该节点返回的角色信息从slave变成master之后,哨兵就知道它已经升级为主节点了。
- 让剩下的从节点指向新主节点
给剩下的从节点发送SALVEOF <ip> <port>
命令,让他们成为新主节点的从节点。
- 通知客户端
哨兵节点会提供消息订阅频道,客户端可以进行订阅。
主从切换完成后,哨兵会向+switch-master
频道发布新主节点的IP地址和端口的消息。
客户端收到消息后可以与新主节点进行通信。
哨兵节点还提供了别的频道,客户端可以监控到主从节点切换过程中发生的各个重要事件,了解主从切换进度。
- 将旧主节点变成从节点
哨兵会继续监视旧主节点,当旧主节点重新上线时,哨兵集群会向他发送SLAVEOF
命令,让它成为新主节点的从节点。
哨兵集群是如何组成的?
Todo..