Redis学习笔记(5)——高可用

Redis多机数据库

  • 主从模式:主服务器和从服务器保存相同的数据。
  • 哨兵模式:哨兵监控主从数据库是否正常,并在主库出现故障时选举从库升级为主库。
  • 集群模式:集群上每个节点存储不同的内容,每个主节点有从节点备份,每个节点之间互相监控,在主库出现故障时选举从库升级为主库。

主从复制

  • 旧版复制功能:Slave向Master发送SYNC命令,Master收到命令开始执行BGSAVE在后台生成RDB文件,并使用缓冲区记录从开始执行的所有写命令;当Master的BGSAVE执行完毕,会将RDB文件发送给Slave,Slave接收并载入RDB文件,将自己的数据库状态更新至Master执行BGSAVE时的数据库状态;Master再将缓冲区中的所有写命令发送给Slave,Slave执行这些写命令,将自己的数据库状态更新至Master当前所处的状态。每当Master执行客户端的写命令从而导致主从不一致,Master需要将写命令发送给Slave执行,从而回到主从一致的状态。
  • 新版复制功能:解决旧版复制在处理断线重复制情况时的低效问题。使用PSYNC命令代替SYNC,具有完整重同步和部分重同步两种模式,部分重同步用于处理断线重复制的情况:Slave在断线后重新连接Master后,Master只发送断开连接期间接收到的写命令,Slave只要接收并执行这些命令就可以更新至Master当前所处的状态。
  • 部分重同步功能的实现:Master和Slave分别维护一个偏移量,通过判断偏移量是否一致,可以判断主从服务器是否处于一致状态;Master维护一个复制积压缓冲区,会将写命令复制到该缓冲区中,为每个字节记录相应的复制偏移量;当Slave连上Master,会把自己的偏移量发送给Master,如果偏移量之后的数据仍存在于缓冲区,执行部分重同步,否则执行完整同步。
  • 心跳检测:Slave每秒发送REPLCONF ACK心跳包给Master,检测主从服务器的连接状态,并报告自己的偏移量;如果Master发现超过一秒没有接收到REPLCONF ACK命令,可知发生了连接问题;如果Master发现Slave的偏移量少于自己的偏移量,会将复制积压缓冲区中对应的数据发送给Slave,使主从达到一致。
  • min-slave配置选项:举例,min-slaves-to-write 3min-slaves-max-lag 10表示Slave少于3个、或者3个Slave的延迟大于等于10秒时,Master拒绝执行写命令。此选项用于减少异步复制的数据丢失。

哨兵模式

  • 主观下线:每个Sentinel以每秒一次的频率向所有与它创建了命令连接的实例发送PING命令,通过返回的命令回复判断实例是否在线。假如Master连续down-after-milliseconds毫秒都向Sentinel1发送无效回复,Sentinel1会把Master标记为主观下线,这称为主观下线。
  • 客观下线:当一个Sentinel把Master标记为主观下线后,会向同样监视这台Master的其他Sentinel询问,其他Sentinel检测到该Master不可用,并且数量达到quorum时,该Sentinel会认为Master进入客观下线状态。当Master被判断为客观下线状态,监视它的各个Sentinel会通过Raft算法选举出一个领头Sentinel对Master执行故障转移操作。
  • 故障转移:领头Sentinel从已下线的Master属下的所有Slave中,选举一个Slave升级为新的Master,然后通过发布订阅模式通知其他Slave,修改配置文件,切换主机;当所有Slave开始复制新的Master时,完成故障转移操作。Sentinel会继续监视已下线的旧Master,当它上线时,将它设置为新Master的Slave。
  • Redis集群的脑裂问题:由于网络问题,Sentinel无法感知到Master的存在,把Slave提升为Master,此时存在两个不同的Master。客户端向原来的Master写入的数据无法同步到新的Master,当Sentinel与原来的Master的连接恢复,将它降级为Slave,会从新的Master同步数据,会造成数据丢失。解决方法:设置min-slave配置选项。

集群

  • 一个集群由若干个节点组成,每个节点之间通过CLUSTER MEET命令进行握手,加入集群。每个节点保存一个clusterState结构,记录当前集群中其它节点的状态。
  • 整个数据库被分为16384个槽,数据库中每一个键都属于这16384个槽中的一个,集群中的每个节点可以处理0到16384个槽。当数据库中的16384个槽都被指派给相应的节点,集群进入上线状态。
  • 每个节点通过clusterNode结构的slots属性得知该节点负责哪些槽,同时会把这些信息发送给集群中的其它节点,来告知其它节点自己负责哪些槽。
  • 当客服端发送与数据库键有关的命令,接收命令的节点先计算出该键属于哪个槽,并判断是否属于自己所负责的槽,如果键所在的槽刚好是当前节点所负责,那么该节点直接执行这个命令;否则会向客户端返回MOVED错误,并告知客户端正确的节点,让它再次发送命令至正确的节点。
  • 集群中的节点分为Master和Slave,其中Master负责处理槽,Slave用于复制某个Master,在Master下线时,代替下线的Master继续处理请求。集群中每个节点会定期向其它节点发送PING消息来检测是否在线,并将没有及时回复PONG消息的节点标记为疑似下线(PFAIL),如果集群中超过半数的节点把一个节点标记为PFAIL,那么这个节点会被标记为已下线(FAIL)。当某个Slave发现它的Master被标记为FAIL,会进行故障转移,从该Master的所有Slave中通过Raft算法选举出新的Master,将旧Master的槽全部指派给自己,向集群中其它节点发送广播,告知它成为了新的Master,完成故障转移。

一致性Hash算法

  • Redis集群分片策略通常是根据key的CRC16校验值对16384取模,当集群中需要添加或删除节点时可能导致大规模的数据迁移,因此可以采用一致性Hash算法来分配key。
  • 一致性Hash算法将整个Hash空间([0, 2^32-1])视为一个圆环,将每个节点的IP地址通过Hash函数映射到圆环上,对于每个key,将其通过Hash函数映射到Hash圆环上,并沿着圆环顺时针行走,遇到的第一个节点就是它应该被分派的节点。
  • 当集群需要增加或删除节点时,只会影响到与它相邻的节点,对其它节点没有影响。
  • 当节点数较少时,容易产生数据分布不均匀的现象,可以通过增设虚拟节点的方式使得数据分布更加均衡。