高可用三种模式
- Ⅰ. 主从热备
- Ⅱ. 哨兵模式
- Ⅲ. 集群模式
- Ⅳ. 番外
总结了Redis的几种高可用方式,对于做后端模块的同学来说,可以根据/或是借鉴其思路做自己的模块可用性部署。
Ⅰ. 主从热备
主从热备,是高可用性中最基础的解决方案,主要作用于主机异常宕机后,可以由备机来接管主机业务。并且此时备机的数据和宕机前的主机是一致的。而主从热备的核心就在于主从复制。
主从复制
主从复制可以分为两步:
- 数据同步
数据同步之部分重同步
从服务器对主服务器的同步可以分为两种情况:
- 首次同步,从服务器首次连接到主服务器并进行数据同步。
- 断线重连后同步。
对于第二种情况,如果每次断线后都进行全量的数据复制可能会造成效率非常底效(如果断线的时间很短,断线期间执行的写命令很少,这种情况下重新执行复制是非常低效的)。
2. 命令传播
心跳检测
心跳检测也是主备中必不可缺少的一环,其主要的作用有:
- 检测主从服务器的网络连接状态。
- 检测数据丢失(心跳检测带偏移量时,主机可检测是否有数据丢失)。
- 其他一些辅助检测(根据模块业务定制辅助检测)等。
Ⅱ. 哨兵模式
Sentinel(哨兵)也是高可用性解决方案,由一个或者多个Sentinel实例组成的Sentinel系统,可以监视任意多个主从服务器,并在被监视的主服务器宕机后,自动将宕机主服务器的从服务器升级为新的主服务器。相比热备模式,可用性更高,功能更加完善,解决了热备中可能存在的一些问题,例如双活。
Sentinel系统处理主机异常的流程
Sentinel实例结构
哨兵的实例使用sentinelState结构表示,记录了哨兵的运行时状态和一些初始化的数据。其中最重要的一个字段为被哨兵监视的主服务器实例集合,采用字典的结构来存放,键为主服务器的名称,值为主服务器对应的实例结构sentinelRedisInstance。
sentinelRedisInstance中包含的属性非常多,其中比较核心的有主服务器的从服务器实例集合、监视主服务器的其他哨兵实例集合、用于检测故障时的一些阙值等。主服务器实例通过配置文件来初始化,主服务器中的从服务器实例集合以及哨兵实例集合则在各模块交流中自动生成。
初始化哨兵实例的最后一步就是建立哨兵和主服务器之间的网络连接。哨兵实例会建立两个连向主服务器的网络连接:命令连接和订阅连接。
哨兵系统中各模块交流过程
频率为10s一次的INFO命令,发送端为Sentinel,接收端为主服务器。主服务器会回复自身的信息以及自身从服务器的信息,Sentinel通过主服务器的回复信息来更新主服务器的实例。如果从服务器的信息不存在主服务器的实例中,则初始化主服务器的从服务器信息实例信息,并建立Sentinel到从服务器的连接(同主服务器一样使用两条网络连接,并且使用INFO命令来获取)。若存在则更新从服务器实例信息。
频率为2s一次发送命令到hello频道,发送端为Sentinel,接收端为订阅了hello频道的其他服务器。Sentinel会将自己的信息和自己监听的服务器信息发送到hello频道中,其他Sentinel在接收到该条信息后,如果和发送端Sentinel监听是同一个服务器,则会更新相应服务器实例信息以及服务器实例包含的Sentinel实例信息(如上哨兵A、哨兵B例子)。并且监听同一个服务器的Sentinel间会相互连接。
频率为1s一次的PING命令,相当于心跳检测,用于判断服务器是否下线。
判断服务器下线流程
选举领头的Sentinel来做故障转移
Sentinel系统选举领头Sentinel采用的方法是Raft算法
。其大致的原理是:
- 每个在线的Sentinel都有被选为领头Sentinel的资格。
- 每个发现主服务器客观下线的Sentinel都会要求其他Sentinel将自己设置为局部领头Sentinel。
- 所有的Sentinel都有一次将某个Sentinel设置为局部领头Sentinel的机会,并且一旦设置在该回合就无法更改了。
- Sentinel设置局部领头Sentinel的规则是先到先得,即最先向目的Sentinel发送设置请求的源Sentinel会被目的Sentinel设置为领头Sentinel。
- 如果某个Sentinel被半数以上的Sentinel设置为局部领头Sentinel,那么这个Sentinel就成为了领头Sentinel。
- 如果在给定的时限内没有一个Sentinel被选举为领头Sentinel,那么会进行下一回合的选举。直到某个回合选举出领头Sentinel。
- 领头Sentinel会做故障转移,故障转移步骤见<< Sentinel系统处理主机异常的流程 >>
新主服务器的挑选
- 过滤从服务器列表中处于下线状态的或是最近五秒内没有回复过领头Sentinel的INFO命令的。
- 过滤从服务器列表中与已下线主服务器连接断开超过指定毫秒的(保证数据最新)。
- 根据优先级或者自己的规则从从服务器列表中选出新的主服务器。
Ⅲ. 集群模式
集群是一种分布式高可用性部署方案。集群通过分片来进行数据共享,并提供复制和故障转移功能。
集群由节点组成
节点使用clusterNode数据结构表示,每个节点会使用clusterNode记录自己的当前状态,并为集群中其他节点都创建相应的clusterNode结构(记录在clusterState结构中)。
每个节点都保存着一个clusterState的结构,该结构记录了节点所处集群的状态信息。并且其中使用字典结构保存了集群中其他节点的信息,key为其他节点的名称,值为其他节点状态clusterNode。
各个独立的节点通过握手的方式连接起来,构成一个包含多个节点的集群。
节点握手的流程,如下:
集群槽管理
Redis集群通过分片的方式来保存数据库中的键值对:整个集群被分为16384个槽,集群中每个节点可以处理0个或最多16384个槽,redis客户端保存的每个键值对都属于这16384个槽其中的一个。只有当所有槽都有节点在处理时,集群才处于上线状态。
注:一些分布式部署中使用哈希环
来做分片管理,redis使用槽来做,目的都是减少局部变更对整体造成的影响。
集群重新分片操作,对集群中单个槽进行重新分片的步骤如下:
节点复制和故障转移
Redis集群中的节点分为主节点和从节点,主节点负责处理槽,从节点则用于复制主节点(复制的方式和热备中复制的方式一致),并在主节点下线后代替下线主节点继续处理命令请求。
一个节点成为某个主节点的从节点,会通过消息发给集群中其他节点,最终集群中所有的节点都会知道,并且他们会更新clusterState结构中保存了该主节点的信息(更新主节点结构中的从节点名单)。
节点故障检测:集群中的每个节点会定期地向集群中其他节点发送PING消息,以此来检测对上是否在线。
当一个主节点进入已下线状态,其从节点会做故障转移。
集群从多个从节点中选择新的主节点方法(raft算法,类似领头Sentinel选举):
- 每个回合,集群中每个负责处理槽的主节点都有一次投票的机会,第一个向主节点要求投票的从节点将获得主节点投票。
- 从节点发现自己的主节点已下线,就会向集群中广播一条消息,收到这条消息并具有投票权的主节点向这个从节点投票。
- 如果集群中有N个具有投票权的主节点,那么当一个从节点收集到N/2+1票时,该从节点就会被选为新的主节点。
- 如果该回合没有选举出新主节点,那么进行下一回合选举。
消息
集群中通过各个节点通过发送和接受消息来进行通信。节点发送的消息主要有以下五种:
- MEET消息,请求接收者加入到发送者所处的集群中。
- PING消息,集群中节点间相互检测是否在线。
- PONG消息,响应MEET消息和PING消息,或是主动向集群广播PONG消息告知其他节点自己的存在或改变(例如故障转移)。
- FAIL消息,将节点标记为已下线的消息。
- PUBLISH消息,当节点接收到PUBLISH命令时,节点会执行这个命令,并向集群广播一条PUBLISH消息。
注:Redis集群中各个节点通过Gossip协议
来交换各种关于不同节点的状态信息。
Ⅳ. 番外
将自己的模块配置成高可用思路总结:将可用性部署做成独立的模块组合到自己的模块中去。依具体的业务而定,不同的模式下业务的执行过程可能不同,但是其核心业务还是一致的,可以剥离出来。尽量做到业务和部署分离。