【Redis】多机数据库的实现

本文详细介绍了Redis的复制功能,包括旧版复制的同步和命令传播,新版复制的PSYNC特性,以及复制实现与心跳检测。进一步讨论了Sentinel哨兵模式,包括其启动初始化、通信连接和故障检测处理。最后,概述了Redis集群的节点、槽指派、故障转移和消息机制,展示了Redis的高可用性解决方案。
摘要由CSDN通过智能技术生成

复制

在redis中,用户可以通过SLAVEOF命令,让服务器区复制另外一个服务器。被复制的服务器为主服务器,执行复制的服务器是从服务器。

旧版(2.8之前)的复制

同步

从服务器向主服务器的同步操作需要向主服务器发送SYNC命令。收到SYNC命名的主服务执行BGSAVE命令,在后台生成一个RDB文件,并使用一个缓冲区记录从现在开始执行的所有写命令。从服务器在写入RDB文件之后,主服务器在发送缓冲区的命令,实现同步。

在这里插入图片描述

命令传播

主服务器需要对从服务器执行命令传播,在执行完自己的写命令之后,发送给从服务器执行。

缺陷

对于断线以后的复制依然是完全重新复制,十分低效。只有在真正必要的时候才有执行SYNC操作。

新版复制功能

具有完整重同步和部分重同步功能。其中部分重同步功能PSYNC用于处理断线以后的复制情况。

PSYNC

三个部分组成:主服务器的复制偏移量和从服务器的复制偏移量,主服务器的复制积压缓存区和服务器的运行ID。

  • 复制偏移量:两个服务器都会维护,每次写入N字节数据,偏移量就+N。如果主从服务器是一致的,两个服务器的偏移量应该一样。
  • 复制积压缓冲区:主服务维护的一个固定长度的,先入先出的队列,默认大小为1MB。主服务器进行命令传播时候,还会将写命令入队到复制积压缓冲区。当主服务器收到PSYNC时,如果offset偏移量之后的数据不在缓冲区,执行完成同步如果在偏移位置在缓冲区,执行部分重同步。缓冲区的大小一般设置为2secondwrite_size_persecond。
  • 运行ID:标识身份。只有之前相互连接过才可能部分复制。

复制的实现

设置主服务器的地址和端口–>建立套接字连接–>发送PING命令–>身份验证–>发送端口信息–>同步–>命令传播。复制过程中,两者互相为对方服务器。

心跳检测

在命令传播阶段,从服务器会默认每秒一次的频率向主服务器发送REPLCOMF ACK <replication_offset>。主要有以下作用:

  • 检测网络连接:如果主服务器超过一秒没收到REPLCONF ACK指令,会意识到出现连接问题。
  • 辅助实现min-slaves:redis可以指定最小从服务器数量和延迟,如在从服务器数量小于3个或者三个从服务器的延迟大于10s时,主服务器拒绝写命令。防止不安全的情况。
  • 检测命令丢失:心跳检测时含有从服务器的偏移量的,主服务器会察觉从服务的缺少的数据,并且重新发送。(类似PSYNC)

Sentinel哨兵模式

哨兵模式时Redis高可用性的解决方案:由一个或多个Sentinel实例组成的哨兵系统可以键是任意多个主服务器及其下属从服务器。并在主服务器下线情况下,选择从服务器升级为主服务器。
在这里插入图片描述

启动并且初始化Sentinel

Sentinel本质是是一个特殊模式的Redis,但是在初始化Sentinel时候,不需要载入RDB和AOF文件。并且Sentinel具有专用的代码。

服务器会初始化一个sentinelState的结构,保存了服务器中与Sentinel有关的状态,如current_epoch。还有一个字典保存了全部被sentinel键是的主服务器,键是主服务器名,值是sentinelRedisInstance类型的指针。

sentinelRedisInstance是一个实例,可以是主从服务器,也可以是另外一个sentinel。该结构保存了各个实例结构的信息,如config_epoch,实例地址等。

通信连接

创建连向主服务器的网络连接

Sentinel会创建两个连向主服务器的异步网络连接:命令连接–专门向主服务器发送命令以及接受回复和订阅连接–订阅_sentinel_:hello频道。

在这里插入图片描述

获取主从服务器信息

Sentinel每隔10s,通过命令向被监视的主服务发送INFO命令。主服务器返回的信息包括了本身信息和从服务器的信息。Sentinel通过这个反馈来发现并更新从服务器的信息。Sentinel端会更新主服务器实例下slave字典的信息。

在发现新的salve之后,sentinel会与salve建立命令连接和订阅连接。同样每个10s进行INFO命令。并更新认识。

向服务器发送和接受订阅

Sentinel每隔2s,以命令连接的方式发送PUBLISH指令,要求每个服务器在hello频道发布消息,包括Sentinel的信息和服务器的信息。(ip,端口号,运行ID,配置纪元)。

同样Sentinel会接收到别的Sentinel发送的消息,这个消息会更新对Sentinel的认识。因此会更新主服务器字典中,监视该服务器的sentinel信息。也就是说,监视同一个主服务器的多个Sentinel可以自动发现对方。并且,两个Sentinel之间也会建立命令连接

故障检测与处理

检测主观下线

默认情况下,Sentinel会以每秒一次的频率向所有与它建立了命令连接的实例发送PING指令。如果在down_after_milliseconds内没有收到有效回复,会被标记为主观下线,打开SRI_S_DOWN标识。多个Sentinel设置的主观下线时长可能不同的。

检测客观下线

在确认了主观下线之后,Sentinel会询问其他连接这个服务的Sentinel,如果接受了足够多的判断,则判定为客观下线,准备进行故障转移。多个Sentinel设置的客观下线投票树可能不同的。

选举领头Sentinel

当主服务器被判断为客观下线时,监视这个服务器的Sentinel会协议选举一个领头Sentinel,处理故障转移。

  1. 每个监视这个下线服务器的Sentinel都可以被选择
  2. 每次进行完选举,无论是否成功,所有Sentinel的config_epoch都加一。
  3. 每个配置纪元,所有Sentinel都只能选择一次且无法更改。
  4. 当源Sentinel发送SENTINEL is-master-down-by-addr <IP> <port><current_epoch><runid>给目标Sentinel,会按照先到先得设置为局部领头。如果成功设置,回复中附有<leader_runid><leader_epoch>
  5. 源Sentinel在检查epoch一致的前提下,计算票数,过半胜利。
  6. 如果选举失败会在一定时间之后重试。

算法的本职是RAFt算法。

故障转移

  1. 在下线服务器的slave中选择一个变为master。
  2. 让其他slave变为新的master的salve。
  3. 让下线服务器变为当前master的salve。

master挑选依据,正常在线,删除5s内没有回复过领头SentinelINFO命令的,删除断开时间超过“主观下线时间”*10毫秒的,然后按照优先级排序,再选择offset最大,如果还一样,选择ID最小的。

在领头Sentinel发送了SLAVEOF no one之后,会以每秒一次的频率发送INFO给升级服务器,观察角色变化。

集群

Redis集群是Redies提供的分布式数据库方案,集群通过水平分片来进行数据共享,并提供复制和故障转移功能。

节点

一个redis集群由多个节点组成,多个节点聚集称为一个集群。CLUSTER MEET <ip> <port>将一个节点添加进入另外一个节点。A节点在联系上了B节点以后会通过Gossip协议传播这一信息给集群中其他节点。

在这里插入图片描述

节点会继续使用redisSever结构保存服务器状态,redisClient保存客户端状态。对于集群的状态分布在clusterNode,clusterLink以及clusterState结构中。

  • clusterState:记录了当前节点的视角下,集群目前的状态,包括集群节点个术和集群的配置纪元(current_epoch)。其中由集群的几点名单,键为节点的名字,值为节点对应的clusterNode结构。
  • clusterNode: 保存了一个节点当前的状态,如创建时间,配置纪元(config_epoch)等。每个节点都会用clusterNode记录自己的状态,并为集群中其他节点创建一个对应的clusterNode结构。
  • clusterLink:保存了连接节点需要的信息,如套接字描述,输入缓冲和输出缓冲。

槽指派

集群通过分片的方式保存数据库中的键值对,整个数据库被分为16384个槽。每个节点可以处理0个或者最多16384个槽。当全部的16384个都有节点在处理时,集群上线;如果有任何一个槽没有被处理,集群都处于下线状态。

槽指派信息是记录在clusterNode中的,其中slots是一个二进制数组,由2048个字节,也就是16384个二进制位。可以按照索引填写1,0区分当前节点是否处理该槽对应的数据。并且每个节点还会传播自己的槽指派情况,告知集群中其他节点自己目前处理哪些内容。其他节点将相应的信息保存到对应节点的clusterNode结构中。

另外还clusterState结构中的slots数组记录了集群中16384个槽的指派信息,每个数组项都是一个clusterNode类型的指针,指向被分配的节点。这种双重的保存,降低了复杂度。culsterState.slots数组记录了集群中所有槽的指派信息,clusterNode.slots录了当前节点的槽指派情况

命令执行

接受命令的节点会计算命令要处理的数据库键在那个槽位,如果是自己的任务就处理,否则返回一个Moved报错,并且引导客户端转向正确的节点。

  • 计算键属于那个槽位:CRC16(key) & 16383得到槽位。
  • 判断节点:clusterState.slots查表O(1)复杂度。如果不是当前节点,给客户端返回错误MOVED <slot> <IP>:<PORT>引导进行跳转。
  • 节点数据库的实现:节点除了将键值对保存在数据库中,还在clusterState中的slots_to_keys跳表结构保存的了槽和键的关系。每个节点的分值是槽号。因此可以进行批量操作。

重新分片

修改键值对在槽中的存储。重新分片可以在线处理,不要下线。重新分片需要通过集群管理软件redis-trib。
在这里插入图片描述

  1. rt给目标节点发送setslot指令,通知目标节点准备好从源节点导入。
  2. rt给源节点发送setslot,通知准备。
  3. 执行图1。并且反复执行2.3直到全部完成复制。
  4. rt向任意节点发送cluster setslot <slot> Node <target_id>,告诉整个槽位的变更。

ASK错误

如果源节点没能在自己的数据库中找到指定的键,可能这个键发生了转移,源节点将发送一个ASK错误,指引客户端转向到目标节点。

实际上在clusterState 的中由数组分别维护当前正在从其他节点导入的槽,和当前节点正在迁移到其他节点的槽。

如果节点没有在自己的数据库中找到key,就回去检查自己的迁移数组,看看是不是在迁移。并且返回ask,指引客户端。ask命令会打开发送命令的客户端的redis_asking标识,这个标识是一次性的,使用之后就会被移除。当客户端收到ask错误进行跳转时候,会先给节点发送asking指令,然后才重新发送想要执行的指令。不然会被拒绝并发送Move指令。

  • ASK错误与MOVED错误

MOVED错误代表槽的控制权已经从一个节点转移到另外一个节点。而ASK节点是一个临时性策略。

复制与故障转移

redis集群中的节点同样分为主节点和从节点。其中主节点处理槽,而从节点则用于复制主节点。

故障检测

集群中每个节点会定期向集群中其他节点发送PING,如果某个节点没有在规定时间内回复PONG。会被标记位疑似下线PFAIL。此时,集群中的节点会相互确认,如果在一个集群中和,半数以上处理槽的主节点,都将某个主节点标注位疑似下线,这个主节点将会变为已下线FAIL,并向集群广播一条关于主节点的FAIL消息。

故障转移

当一个从节点发现自己的主节点出现了已下钱的标识,会开始进行故障转移:

  1. 该主节点的所有从节点中会由一个从节点被选为主机点,执行SLAVEOF no one.
  2. 新的主节点会撤销所有下线节点的槽指派,改为指派自己。
  3. 向集群广播PONG消息,让集群其他节点只道从节点上位了,并且接管了槽。

选举主节点的方法

  1. 集群的某个节点开始故障转移时,集群配置纪元curruetepoch都家以。
  2. 每个纪元中,每个主节点都只有一次投票机会。先到先得。
  3. 当从节点发现自己父节点先下线,向集群广播一下CLUSTERMSG_TYPE_FAILOVER_AUTH_REQUEST消息,要求收到的主节点投票。
  4. 可以进行投票的主节点会回复消息。
  5. 故障处理由延迟时间: 500ms+random(0-500)+rank*1000ms。固定延迟保证达成共识已经下线,random保证去同步性,rank是从节点之间通信,得到最新的offset的节点rank最低。
  6. 只要的票大于所有主节点的一半,称为成功。

消息

集群中由五种 消息:

  • MEET:请求接受者加入到发送者当前的集群当中。
  • PING:集群中的每个节点默认每个一秒钟就会从已知节点列表中随机选择五个节点,然后对这5哥节点中最长时间没发送过PING的节点发送PING。此外,如果节点A最后一次收到节点B的PONG消息时长超过了cluster-node-timeout的一半,会再次发送PING消息。防止更新滞后。
  • PONG:收到MEET和PING消息后进行回复。此外也可以利用集群广播发送自己的PONG消息,让集群其他节点立刻刷新对自己的认知。如完成某一次故障转移之后。
  • FAIL:当主节点A判断另外一个主节点B已经下线时,节点A会向集群广播一条B的FAIL消息,收到消息的节点都会立刻将B标记为已下线。
  • PUBLISH:当节点收到PUBLISH命令时,节点会执行这个任务,并且集群广播一条PUBLISH命令,所有接收到这个消息的节点都会执行相同的命令。

前三种消息都是采用的Gossip协议来实现的节点之间的状态信息的交换。节点通过消息头判断消息的具体类型。FAIL消息因为需要立即让集群所有节点知晓,因此单纯采用Gossip协议传播会存在一定的延迟,因此采用集群广播。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值