• Redis设计与实现(第三部分):多机数据库的实现


    Redis设计与实现(第三部分):多机数据库的实现

    三、多机数据库的实现

    14. 复制

    14.1 旧版复制功能的实现(2.8之前)

    第一步:同步

    1. 客户端向从服务器发送SLAVEOF命令
    
    2. 从服务器向主服务器发送SYNC命令
    
    3. 收到SYNC命令的主服务器执行BGSAVE命令,在后台生成一个RDB文件,并使用一个缓冲区记录从现在开始执行的所有写命令。
    
    4. 主服务器BGSAVE命令执行完毕时,主服务器将生成的RDB文件发送给从服务器
    
    5. 从服务器接受并载入这个RDB文件
    
    6. 主服务器发送缓冲区的命令给从服务器执行,主从数据库一致
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    第二步:命令传播

    1. 主从数据库一致后,主服务器发生修改
    
    2. 主服务器将自己执行的写命令,也即是造成主从服务器不一致的那条写命令,发送给从服务器执行
    
    3. 从服务器执行相同的写命令,主从服务器将再次回到一致状态
    
    • 1
    • 2
    • 3
    • 4
    • 5

    14.2 旧版存在的问题

    初次复制,从服务器向主服务器发送SYNC命令,完整复制,没有问题
    
    但是,处于命令传播阶段的主从服务器因为网络原因而中断了,再次连上继续复制,也是发送SYNC命令完整复制,效率低
    
    • 1
    • 2
    • 3

    14.3 新版复制功能的实现(2.8开始)

    使用PSYNC命令代替SYNC命令来执行复制时的同步操作。

    PSYNC命令具有 完整重同步 和 部分重同步 两种模式:完整重同步和SYNC命令的执行步骤基本一样,部分重同步则用于处理断线后重复制情况

    部分重同步的实现:

    1. 复制偏移量:

    	主从服务器分别维护一个复制偏移量,主每次发送N个字节,从每收到N个字节,就将自己的复制偏移量的值加上N
    
    	通过对比主从服务器的复制偏移量,可以很容易地知道主从服务器是否处于一致状态
    
    • 1
    • 2
    • 3

    2. 复制积压缓冲区:

    	是由主服务器维护的一个固定长度的先进先出的对列,默认1MB,当入队元素的数量大于队列长度时,最先入队的元素会被弹出
    
    	当主服务器进行命令传播时,它不仅会将写命令发送给所有从服务器,还会将写命令入队到复制积压缓冲区里面
    
    	当从服务器重新连上主服务器时,从服务器会通过PSYNC命令将自己的复制偏移量offset发送给主服务器
    
    	主服务器会根据这个复制偏移量来决定对从服务器执行何种同步操作
    
    	如果偏移量之后的数据存在于缓冲区里面,就执行部分重同步操作,反之执行完整重同步
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    3. 服务器运行ID

    每个Redis服务器,都会有自己的运行ID,在服务器启动时自动生成
    
    当从服务器对主服务器进行初次复制时,主服务器会将自己的运行ID传送给从服务器,从服务器将这个运行ID保存起来
    
    当从服务器断线重连时,从服务器向主服务器发送之前保存的ID
    
    如果这个ID和要连接的主服务器ID相同,则表示之前就有连接,尝试部分重同步,反之执行完整重同步
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    14.4 PSYNC命令的实现

    在这里插入图片描述

    14.5 复制的实现步骤

    1. 设置主服务器的地址和端口
    
    2. 建立套接字连接,关联事件处理器
    
    3. 发送PING命令检查连接是否正常
    
    4. 根据设置决定是否进行身份验证
    
    5. 发送端口信息,从服务器向主服务器发送从服务器的监听端口号
    
    6. 开始同步
    
    7. 命令传播
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    14.6 心跳检测

    在命令传播阶段,从服务器默认会以每秒一次的频率,向主服务器发送 REPLCONF ACK 命令

    作用

    检测主从服务器的网络连接状态
    
    辅助实现min-slaves选项
    	min-slaves-to-write 和 min-slaves-max-lag 两个选项可以防止主服务器在不安全的情况下执行写命令
    	
    	min-slaves-to-write 3
    	min-slaves-max-lag 10
    	表示从服务器少于3个,或者三个服务器延迟都大于等于10秒,主服务器拒绝写命令
    
    检测命令丢失
    	因为网络故障,主服务器传输写命令丢失,导致主从偏移量不同
    
    	主服务器根据从服务器发送的复制偏移量,在复制积压缓冲区里面找到从服务器缺少的数据重新发给从服务器
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    15. Sentinel

    Sentinel是Redis的 高可用性解决方案:

    由一个或多个Sentinel实例组成的Sentinel系统可以监视任意多个主服务器,以及这些主服务器属下的所有从服务器,并在被监视的主服务器进入下线状态时,自动将下线主服务器属下的某个从服务器升级为新的主服务器,然后由新的主服务器代替已下线的主服务器继续处理命令请求。

    在这里插入图片描述

    1. Sentinel启动步骤

    1)初始化服务器。
    
    2)将普通Redis服务器使用的代码替换成Sentinel专用代码。
    
    3)初始化Sentinel状态。
    
    4)根据给定的配置文件,初始化Sentinel的监视主服务器列表。
    
    5)创建连向主服务器的网络连接。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    2. 获取主从服务器信息

    Sentinel默认会以每十秒一次的频率,通过命令连接向被监视的主服务器发送INFO命令
    通过分析INFO命令的回复来获取主服务器的当前信息。
    
    当Sentinel发现主服务器有新的从服务器出现时,Sentinel除了会为这个新的从服务器创建相应的实例结构之外
    Sentinel还会创建连接到从服务器的命令连接和订阅连接。
    
    • 1
    • 2
    • 3
    • 4
    • 5

    3. 检测下线状态

    主观检查
    	默认情况下,Sentinel以每秒一次的频率向所有与它创建了命令连接的实例发送PING命令
    	通过实例返回的PING命令回复来判断实例是否在线。
    
    客观检查
    	当Sentinel将一个主服务器判断为主观下线之后,会向监视这一主服务器的其他Sentinel进行询问
    	确认它们是否也认为主服务器已经进入了下线状态
    	
    	如果从其他Sentinel那里接收到足够数量的已下线判断
    	Sentinel就会将从服务器判定为客观下线,并对主服务器执行故障转移操作。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    4. 选举领头Sentinel

    1) 每个Sentinel节点都有资格成为领导者,当它确认主节点主观下线时,会向其他Sentinel节点发送命令,要求将自己设置为领导者
    
    2) 收到命令的Sentinel节点,如果没有同意过其他Sentinel节点的请求命令,将同意该请求,否则拒绝
    
    3) 如果该Sentinel节点发现自己的票数已经大于半数,那么它将成为领导者
    
    4) 如果此过程没有选举出领导者,将进入下一次选举
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    5. 故障转移
    在选举出新的领导者之后,他负责故障转移

    1)在已下线主服务器属下的所有从服务器里面,挑选出一个从服务器,并将其转换为主服务器。
    
    2)让已下线主服务器属下的所有从服务器改为复制新的主服务器。
    
    3)将已下线主服务器设置为新的主服务器的从服务器,当这个旧的主服务器重新上线时,它就会成为新的主服务器的从服务器。
    
    • 1
    • 2
    • 3
    • 4
    • 5

    16. 集群

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

    16.1 节点

    一个Redis集群通常由多个节点组成,开始每个节点都是相互独立的,我们必须将各个独立的节点连接起来,构
    成一个包含多个节点的真正集群。

    向一个节点node发送 CLUSTER MEET   命令,可以让node节点与ip和port所指定的节点进行握手
    当握手成功时,node节点就会将ip和port所指定的节点添加到node节点当前所在的集群中。
    
    Redis服务器在启动时会根据 cluster-enabled 配置选项是否为 yes 来决定是否开启服务器的集群模式
    
    • 1
    • 2
    • 3
    • 4

    在这里插入图片描述

    CLUSTER MEET命令的实现

    1)节点A会为节点B创建一个clusterNode结构,并将该结构添加到自己的clusterState.nodes字典里面。
    
    2)节点A将根据CLUSTER MEET命令给定的IP地址和端口号,向节点B发送一条MEET消息(message)。
    
    3)节点B将接收到节点A发送的MEET消息,节点B会为节点A创建一个clusterNode结构,并将该结构添加到自己的clusterState.nodes字典里面。
    
    4)节点B将向节点A返回一条PONG消息。
    
    5)节点A将接收到节点B返回的PONG消息,通过这条PONG消息节点A可以知道节点B已经成功地接收到了自己发送的MEET消息。
    
    6)节点A将向节点B返回一条PING消息。
    
    7)节点B将接收到节点A返回的PING消息,通过这条PING消息节点B可以知道节点A已经成功地接收到了自己返回的PONG消息,握手完成。
    
    然后,节点A会将节点B的信息通过Gossip协议传播给集群中的其他节点,让其他节点也与节点B进行握手。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    在这里插入图片描述

    16.2 槽指派

    Redis集群通过分片的方式来保存数据库中的键值对

    集群的整个数据库被分为16384个槽
    
    当数据库中的16384个槽都有节点在处理时,集群处于上线状态,否则处于下线状态
    
    clusterNode结构的slots属性和numslot属性记录了节点负责处理哪些槽
    	slots属性是一个二进制位数组,长度2048个字节,0~16383
    	根据索引i上的二进制位的值来判断节点是否负责处理槽i,1负责,0不负责
    
    一个节点除了会将自己负责处理的槽记录在clusterNode结构的slots属性和numslots属性之外
    还会将自己的slots数组通过消息发送给集群中的其他节点
    因此,集群中的每个节点都会知道数据库中的16384个槽分别被指派给了集群中的哪些节点
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    16.3 在集群中执行命令

    流程如图
    在这里插入图片描述

    集群节点保存键值对以及键值对过期时间的方式与单机版相同
    
    区别:节点只能使用0号数据库,而单机Redis服务器则没有这一限制。
    
    • 1
    • 2
    • 3

    16.4 重新分片

    Redis集群的重新分片操作可以将任意数量已经指派给某个节点的槽改为指派给另一个节点,并且相关槽所属的键值对也会从源节点被移动到目标节点, 该操作可以线上进行,无需下线,并且源节点和目标节点都可以继续处理命令请求。

    对槽slot进行重新分片的过程
    在这里插入图片描述

    16.5 ASK错误

    在进行重新分片期间,有一部分已经转移到目标节点,另一部分还在源节点时,这时客户端发来一条有关正在迁移槽的命令,怎么办呢?

    源节点会先在自己的数据库里面查找指定的键,如果找到的话,就直接执行客户端发送的命令。
    
    否则,源节点将向客户端返回一个ASK错误,指引客户端转向正在导入槽的目标节点,并再次发送之前想要执行的命令。
    
    • 1
    • 2
    • 3

    在这里插入图片描述

    16.6 复制与故障转移

    Redis集群中的节点分为主节点和从节点,主节点用于处理槽,从节点用于复制某个主节点,并在被复制的主节点下线时,代替下线主节点继续处理命令请求。

    向一个节点发送 CLUSTER REPLICATE  命令,让这个节点成为node_id的从节点,并开始复制主节点
    
    这一行为会通过消息发送给集群中的其他节点,让整个集群都知道
    
    主节点也会记录他的所有从节点名单
    
    • 1
    • 2
    • 3
    • 4
    • 5
    集群中的每个节点都会定期地向集群中的其他节点发送PING消息,检测对方是否在线,
    
    如果接收PING消息的节点没有在规定的时间内,向发送PING消息的节点返回PONG消息,
    
    那么发送PING消息的节点就会将接收PING消息的节点标记为疑似下线
    
    集群中的各个节点会通过互相发送消息的方式来交换集群中各个节点的状态信息
    
    在集群中,半数以上负责处理槽的主节点都将某个主节点报告为疑似下线,那么这个主节点将被标记为已下线,并广播给其他主节点
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    故障转移步骤

    1)出现故障的主节点的所有从节点里面,会有一个从节点被选中。
    
    2)被选中的从节点会执行 SLAVEOF no one 命令,成为新的主节点。
    
    3)新的主节点会撤销所有对已下线主节点的槽指派,并将这些槽全部指派给自己。
    
    4)新的主节点向集群广播一条PONG消息,宣布自己由从节点变成了主节点,并接管了原本主节点负责处理的槽
    
    5)新的主节点开始接收和自己负责处理的槽有关的命令请求,故障转移完成。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    OVER(∩_∩)O~

  • 相关阅读:
    区块链 - 各个国家Web3的现状与趋势
    05_不同路径2(带障碍物版)
    基于知识图谱的职位推荐系统的设计与实现
    PolarDB 卷来卷去 云原生低延迟强一致性读 1 (SCC READ 译 )
    那些测试行业的细分岗位,你知道多少?薪资又如何?
    FLStudio21汉化破解激活版下载,Fl Studio 2024中文破解版激活补丁
    二维矩阵的DFS算法框架
    常用代码模板1——基础语法
    Django—图片验证码
    uniapp制作--简单的tab切换
  • 原文地址:https://blog.csdn.net/NICK_53/article/details/125992733