学习《Redis设计与实现》Chapter4

复制模式

通过SLAVEOF命令或者设置slaveof选项,让一个服务器去复制另一个服务器,被复制的服务器为主服务器,复制的服务器为从服务器。通过复制模式,能让从服务器保持和主服务器的数据一致。

实现

redis的复制功能分为同步和命令传播两个操作。

同步

当客户端向从服务器发送SLAVEOF命令,要求从服务器复制主服务器时,从服务器要先将自己的数据库状态更新至主服务器的数据库状态。这里需要分成两种情况

1.初次复制

· 从服务器向主服务器发送SYNC命令。

· 主服务器收到SYNC命令后执行BGSAVE命令,生成RDB文件后,开启一个缓冲区记录从选择开始执行的所有写命令。

· RDB文件生成完后将文件发送给从服务器,从服务器接收该文件后载入更新自己的数据库状态。

· 主服务器将缓冲区里面的所有写命令发送给从服务器,从服务器执行这些命令后数据库状态与主服务器的一致。

2.断线后重新复制

在老版本断线后重新复制的流程和初次复制一样,但这很消耗资源,而且真正要复制的只有部分,所以提出了PSYNC命令来做部分重同步。部分重同步的功能由复制偏移量、复制积压缓冲区、服务器运行ID组成。

复制偏移量

复制模式下每个服务器都会维护自己的一个复制偏移量。刚同步完成时主从服务器的复制偏移量是一样的,假设是N。而后主服务器每向从服务器发送n个字节的数据时,就将自己的复制偏移量加n。从服务器收到来自主服务器的n个字节的数据时,也将自己的复制偏移量加n。以此来判断主从数据库状态是否一致。

复制积压缓冲区

由主服务器维护的一个定长的先进先出队列,大小默认1M。主服务器执行写命令时,不仅会命令传播给从服务器,还会写入该队列,以此来缓存一部分最近传播的写命令。

服务器运行ID

每个redis服务器自己专属的运行ID,用作唯一标识。

过程

· 从服务器向主服务器发送PSYNC命令时,会带上断线前复制的主服务器的运行ID和自身的复制偏移量。

· 主服务器先判断从服务器之前复制的主服务器是不是自己,如果不是的话相当于初次复制,如果是的话再判断从服务器的复制偏移量是不是仍存在于复制积压缓冲区中。如果是的话,就将缓冲区内从服务器复制偏移量之后的数据发送给从服务器进行部分重复制;如果不在了,那就只能重新全量复制,也就相当于初次复制。

所以对于复制积压缓冲区的大小设定可以根据实际情况来做估算。最小大小一般为从服务器断线重连的平均时长乘以主服务器平均每秒产生的写命令数据量。安全起见可以设置为最小大小的两倍。

命令传播

同步完成后,主从服务器要保持状态一致,就依赖于命令传播。主服务器每次执行了写命令,都会对从服务器执行命令传播操作,让从服务器也去执行该命令,使二者数据库状态重新一致。

min-slaves配置

Redis的min-slaves-to-write和min-slaves-max-lag两个选项可以防止主服务器在不安全的情况下执行写命令。如果从服务器的数量小于min-slaves-to-write,或者min-slaves-to-write个从服务器的延迟都大于等于min-slaves-max-lag,主服务器将拒绝执行写命令。

心跳检测

在命令传播阶段,从服务器会每秒一次向主服务器发送ACK命令,用于检测主从服务器的网络连接状态、检测命令丢失和辅助实现min-slaves配置。

ACK命令会带上从服务器的复制偏移量,主服务器以此来判断从服务器是否发生了命令丢失。如果有的话,根据传来从服务器的复制偏移量,将缺失的那部分数据重新发送给从服务器,使二者数据库状态重新一致。

哨兵模式

哨兵模式的提出主要是为了解决单纯的复制模式下,如果主服务器挂了,需要手动将一台从服务器升级为主服务器并通知其它从服务器的弊端。哨兵本身其实就是一台特殊的redis服务器,用于将复制模式的问题自动化处理。

原理

· 先初始化一个哨兵,这个哨兵持有所监视的主服务器的一些信息。这些信息存在一个master字典上,键是主服务器的名字,值是该主服务器信息对应的结构体。

· 与主服务器构建网络连接。一个命令连接做监视,一个订阅连接。

· 每10秒一次向主服务器发送INFO命令来获取主服务器当前的信息。这个信息会返回主服务器属下的所有从服务器的信息,以此来实现哨兵自动发现从服务器,从服务器的相关信息记录在该主服务器信息对应的结构体的slaves字典上,键是从服务器的名字,值是从服务器信息对应的结构体。

· 与从服务器构建网络连接。同样是命令连接和订阅连接。

· 默认情况下,每2秒,哨兵通过与主从服务器建立的订阅链接,在订阅的频道内发送一条命令。该命令包含了哨兵自身的信息和主服务器的信息。对于监视同一个服务器的其他哨兵来说,一个哨兵在频道内发送的信息能被其他哨兵接收,这些信息会被用于更新其他哨兵对发送信息的哨兵的认知,也会更新其他哨兵对被监视服务器的认知。在主服务器信息对应的结构体中,sentinels字典包含了所有监视自己的哨兵的信息,键是哨兵的名字,值是哨兵信息对应的结构体。

· 当哨兵通过频道信息发现了其它哨兵,它不仅会更新sentinels字典,还会在哨兵之间互相建立一个命令连接,以此来实现哨兵主观下线检测和客观下线检测。

检查主观下线状态

默认情况下,哨兵会每秒一次的向所有与它建立了命令连接的实例发送PING命令来判断对方是否在线。如果一个实例在down-after-milliseconds毫秒内连续返回无效恢复,哨兵就会认为这个实例已经进入主观下线状态。该配置对每个哨兵而言都是独立的,所以每个哨兵对主观下线的判定是不同的。

检查客观下线状态

当哨兵标记一个实例为主观下线状态后,它会向其它监视这一实例的哨兵进行询问。如果其它哨兵有足够数量认为该实例是主观下线或客观下线状态,那么这个实例就会判定为客观下线。如果这是一个主服务器,就会执行故障转移。数量多设置对每个哨兵而言都是独立的,所以每个哨兵对客观下线的判定是不同的。

当一个主服务器被判定为客观下线,监视该服务器的所有哨兵会选举出一个领头哨兵,由该领头的哨兵执行故障转移。

故障转移

· 在已下线的主服务器的从服务器列表中挑选一个从服务器转换为主服务器。选取优先级依次为未下线、最近5秒内与领头哨兵通信过、与主服务器没有过早断开、从服务器的优先级权重设置、复制偏移量、运行ID。

· 修改剩余从服务器的复制目标为新选出的主服务器,并进行同步。

· 将旧的主服务器变为从服务器。但是因为旧的主服务器已下线,该操作是保存在该主服务器对应的结构体上。当旧的主服务器重新上线,就会变为新的主服务器的从服务器。

集群

哨兵模式虽然实现了高可用和读写分离,但是每台服务器存的都是相同的数据,在数据量更大的情况下,依旧存在性能瓶颈。所以集群的概念提出就是针对该性能瓶颈的优化。  

一个redis集群是由多个节点组成。每个节点都有一个ClusterNode结构保存了节点的当前状态,也持有一个ClusterState结构,记录了该节点视角下集群的状态信息。

CLUSTER MEET

通过CLUSTER MEET命令可以让节点A将另一个节点B加入到节点A所在的集群内。节点A和B握手成功的话,会在彼此的ClusterNode结构内记录对方的ClusterNode。然后通过Gossip协议传播给集群内的其他节点,让其它节点也去和节点B握手。最终节点B就会被集群内的其他节点认识。

槽指派

集群通过分片的方式保存数据库中的键值对,集群一共有16384个槽,只有所有的槽都有节点在处理,集群才会被视作上线状态。ClusterNode结构记录了该节点负责处理哪些槽,因为每个节点都会持有其它节点的ClusterNode结构,所以节点负责的槽在节点之间是透明的。

ClusterState结构中用数组记录了集群所有16384个槽的指派信息,数组的下标就是槽的位置,该下标对应的元素就是槽被指派给的节点。

集群中执行命令

· 计算键属于哪个槽。CRC16(key) & 16384得到槽号。这个就是集群实现分片的算法。

· 判断该键所属的槽是否是本节点负责。如果是的话,直接执行;如果不是,节点向客户端返回MOVED错误,然后通过ClusterState记录的槽数组O(1)找到负责的节点,指引客户端转到正确的节点上。

节点数据库的实现

集群节点保持键值对和过期时间的方式与单机数据库完全一样。数据库方面唯一的区别是节点只能使用0号数据库,单机则没有限制。

节点除了用数据库保存键值对以外,还会用ClusterState结构中的一个跳跃表来保存槽和键之间的关系。该跳跃表每个元素的成员是键,分数是槽号。

重新分片

由redis的集群管理软件redis-trib负责执行。对每个给定的槽,以固定数量的键值对为一个最小单元,原子性的从源节点迁移到目标节点。槽内的键值对转移完毕后,同时会通过消息发送至整个集群,通知其它节点槽已经被指派给了目标节点。

ASK错误

在重新分片期间,可能会出现槽内的键值对只转移了部分,而客户端发送了一个该槽的键相关的命令来。源节点会先在自己的数据库中查找,如果找到的话就直接执行客户端发送的命令;如果没找到,就可能迁移到了目标节点,源节点就向客户端返回ASK错误并指引客户端转向到目标节点再次发送要执行的命令。

复制

集群中的主节点用于处理槽,从节点用于复制某个主节点,并在主节点下线时升级为主节点继续处理命令请求。当一个节点成为从节点时,同样会通知其它节点,让所有节点都对该节点产生认知。

故障检测

每个节点都会定期向其它节点发送PING消息来检测对方是否在线。超过半数的主节点认为某个主节点是疑似下线状态时,该节点就会在集群内被广播为下线状态,然后所有节点更新该节点的状态为下线状态。

故障转移

相较于哨兵模式,多了一步槽的重新指派。要将已下线的主节点的槽指派撤销并指派到自己身上并通知集群内其他节点。

消息

集群中的节点通过发送和接收消息来通信,常见的消息包括MEET,PING,PONG,PUBLISH,FAIL。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值