当前位置: 首页 > news >正文

【Redis】主从复制分析-基础

1 主从节点运行数据的存储

在主从复制中, 对于主节点, 从节点就是自身的一个客户端, 所以和普通的客户端一样, 会被组织为一个 client 的结构体。

typedef struct client {// 省略
} client;

同时无论是从节点, 还是主节点, 在运行中的数据都存放在一个 redisServer 的结构体中, 定义如下:

struct redisServer {// 省略
};

在主从复制中的 client 和 redisServer 都是用来存储运行中的一些数据。
具体里面存储的数据是什么, 在后面的源码分析中, 逐渐了解。

2 主从节点复制中的状态枚举

2.1 从节点自身状态的状态枚举

整个主从复制是一个复杂的过程, 所以在从节点中, 维护了一套状态, 通过状态来判断下一步的流程 (有点像状态模式)

// 没有开启主从复制功能, 默认的状态
#define REPL_STATE_NONE 0// 开启了主从复制, 但是还没连接上主节点
// 执行了 slaveof/replicaof 命令时, 从节点的切换成的状态 
#define REPL_STATE_CONNECT 1// 正在连接主节点, 从节点开始连接主节点
#define REPL_STATE_CONNECTING 2/* --- 握手阶段的状态开始, 整个握手过程必须按照下面的顺序进行 --- */// 从节点发送了 ping, 等待主节点 pong  应答 (正常情况, 主节点会回复一个 pong)
#define REPL_STATE_RECEIVE_PONG 3// 准备发送认证密码给主节点
#define REPL_STATE_SEND_AUTH 4// 等待主节点响应认证结果应答
#define REPL_STATE_RECEIVE_AUTH 5// 准备发送从节点的监听的端口
#define REPL_STATE_SEND_PORT 6 // 等待主节点响应收到从节点端口
#define REPL_STATE_RECEIVE_PORT 7// 发送主从复制配置的监听的 IP 地址
#define REPL_STATE_SEND_IP 8 // 等待主节点响应收到从节点的 IP 地址
#define REPL_STATE_RECEIVE_IP 9// 准备发送从节点支持的同步能力 
#define REPL_STATE_SEND_CAPA 10 // 等待主节点响应收到支持的同步能力的应答
#define REPL_STATE_RECEIVE_CAPA 11// 向主节点发送 psync 命令, 请求同步复制
#define REPL_STATE_SEND_PSYNC 12 // 等待 psync 应答
#define REPL_STATE_RECEIVE_PSYNC 13/* --- 握手阶段的状态结束 --- */// 正在接收从主节点发送过来的 RDB 文件
#define REPL_STATE_TRANSFER 14 // 已经连接状态
#define REPL_STATE_CONNECTED 15 

2.2 主节点保存从节点的状态枚举

对于主节点而言, 需要知道从节点当前的状态的, 好进行对应的操作, 但是不需要那么详细, 主节点也维护了一套从节点运行时的状态


// 等待 bgsave (生成 RDB 文件的函数) 的开始
#define SLAVE_STATE_WAIT_BGSAVE_START 6// 等待 bgsave 的结束, 也就是 RDB 文件的创建结束
#define SLAVE_STATE_WAIT_BGSAVE_END 7// 发送一个 RDB 文件到从节点
#define SLAVE_STATE_SEND_BULK 8// 从节点在线
#define SLAVE_STATE_ONLINE 9

3 从节点复制能力

3.1 全量复制和部分复制

整个主从复制, 大体可以概括为 3 种情况

  1. 一开始, 主从节点建立连接, 这时候主节点需要将自身所有的数据全部同步给从节点
  2. 运行中, 主节点需要将自己处理的命令, 发送一份给从节点, 这样才能保证主从的一致
  3. 运行中, 出现了网络波动, 服务重启等情况, 重新恢复正常时, 主从需要重新通过复制, 让彼此的数据重新保持一致

第一步
主从建立了连接, 主节点会将自身所有的数据生成为一个 RDB 的文件, 然后以 EOF 的流格式发送给从节点。
当然, Redis 在 2.8.18 版本开始支持无盘复制, 子进程直接将 RDB 通过网络发送给从服务器, 不使用磁盘作为中间存储。
主要是防止比较低速的磁盘, 写入缓慢, 影响到整个应用。
这个主节点将所有数据发送给从节点的操作, 叫做全量复制

第二步
在运行中, 主节点处理完了命令, 会遍历自身维护的所有的从节点, 将自身执行的命令发送给所有状态符合的从节点, 保证数据的一致。
可以看出来, 第一, 二步的操作是一个比较简单的过程, 而第三步, 在兼容性能的情况下, 将会是一个复杂的过程。

第三步
主从节点之间出现网络波动, 从节点重启等行为后, 主从之间就可能出现数据不一致。
在 Reids 2.8 版本之前, Reids 的操作就是通过在来一次全量复制, 保证了主从节点数据的一致性。

全量复制 我们可以很容易就想到这是一个耗时, 耗资源的过程, 比如 fork 子进程, RDB 文件生成, 数据发送。
所以为了尽量避免全量复制的出现, 在 Redis 2.8 版本, 引入了一个复制积压缓冲区的缓存区, 主节点执行的命令, 会先保存一份到这个缓存区
(这个缓存区是一个环形的数组, 从头写到尾, 写满了, 重新回到头, 继续写, 新的数据覆盖掉旧的, 同时所有的从节点共用一个缓冲区)。

Alt '复制积压缓冲区'

大体的实现如下

  1. 主节点启动的时候, 会生成一个 run_id (用来确保复制的主节点的唯一性)
  2. 同时生成一个复制积压缓冲区, 并且有一个变量, 记录着缓冲区中当前最旧的数据的位置, 假设为 min, 假设复制积压缓冲区的容量为 len
  3. 从节点保存着主节点生成的 run_id, 同时维持着一个变量, 当前自身最新的数据在复制积压缓冲区的哪个位置, 也就是复制偏移量, 假设为 offset
  4. 当前主从复制出现问题时, 主从重新建立连接后, 从节点会将 run_id 和 offset 发送给主节点
  5. 主节点收到后, 如果 run_id 和自身的一致, 同时 min <= offset <= min + len, 也就是需要开始复制的位置的数据, 可以在缓存区中找到, 那么从这个位置进行部分复制
  6. 主节点如果收到的 run_id 不一致, 或者 offset < min 或者 offset > min + len, 也就是需要开始复制的位置不在缓冲区中, 直接进行全量复制

Alt '部分复制过程'

这个的功能叫做 psync, 可以理解为 部分复制, 这个功能可以减少全量复制的发生。
但是这个功能有些问题, 就是从节点需要维护好 run_id, run_id 需要和从节点的一致, 同时 offset 需要在复制积压缓存中, 这其中

  1. slave 维护性重启, run_id 和 offset 会丢失
  2. 主节点故障转移, run_id 会改变
    都会导致从节点直接全量复制

所以在 Redis 4.0 针对上面的 2 种情况进行了优化, 使其在上面说的情况下, 可以进行使用部分复制, 这个升级的功能也被叫做 psync2
涉及的几个名词

  1. replid1, 每个 Redis 实例启动就会自动产生的一个 id, 这个实例变成从节点, 会被替换为主节点的 replid1
  2. replid2, 默认初始为 0, 用于存储上次主节点的 replid1

当然还要其他的情况, 会导致部分复制的失效

  1. 直接重启主节点, 这是复制积压缓冲区的数据丢了, 没法部分复制

所以 psync2 只是针对上面的 2 种情况进行了优化, 其他的情况, 还是会直接进入到全量复制

3.2 从节点重启的部分复制

在 Redis 4.0 中

  1. Redis 关闭时, 会把复制相关的信息 replid1 和 offset 作为辅助信息保存到 RDB 文件
  2. Redis 重启时, 会将从 RDB 文件中重新加载对应的复制信息到对应的字段
  3. 在进行同步时, 将 replid 和 offset 发送给主节点, 尝试进行部分复制
3.3 主节点故障转移的部分复制

当前的场景主要是在 Redis Cluster 中的故障转移情景的分析, 在直接使用主从复制中, 主节点重启, 挂了等, 都无法避免全量复制

在 Redis 4.0 中

  1. 使用了 2 组 replid 和 offset
  2. 从节点也会开启复制积压缓冲区功能, 以便从节点故障切换变化为主节点后, 其他落后的从节点可以进行同步
  3. 第一组 replid 和 offset, 如果是主节点, 表示的是自己的 replid 和复制偏移量 offset, 从节点表示的是主节点的 replid (确保级联时, replid 都是一致的) 和自身同步主节点的赋值偏移量
  4. 第二组 replid 和 offset, 无论是主从, 都表示自己的上次主节点的 replid 和 offset, 没有则默认为 0 和 -1
  5. 主节点发生了故障切换时, 会将自身的第一组 replid 和 offset 复制给第二组 replid 和 offset
  6. 这时候, 无论是一主一次还是一主多从, 都会有一个自身的从节点变为主节点, 自身变为从节点, 后面故障恢复了, 可以根据自身的第二组 replid 和 offset 进行恢复
  7. 当然不只是主从关系, 级联复制, 也适用

上面说的 3 个版本的复制能力, 在代码中分别叫做 EOF, psync1, psync2

复制能力的定义如下:

// 什么能力都不支持
#define SLAVE_CAPA_NONE 0// 支持 EOF, 支持全量复制, 可以解析 RDB EOF 流式处理格式
#define SLAVE_CAPA_EOF (1<<0) // 支持 psync2, 支持部分复制
#define SLAVE_CAPA_PSYNC2 (1<<1)

对于 Redis 从节点, 可能因为版本问题, 存在着复制能力不一致的情况, 所以在主从复制开始的阶段, 从节点需要将自身支持的复制能力发送给主节点,
主节点才能以正确的方式同步数据给从节点。

4 参考

redis psync1和psync2 同步详解

相关文章:

【Redis】主从复制分析-基础

1 主从节点运行数据的存储 在主从复制中, 对于主节点, 从节点就是自身的一个客户端, 所以和普通的客户端一样, 会被组织为一个 client 的结构体。 typedef struct client {// 省略 } client;同时无论是从节点, 还是主节点, 在运行中的数据都存放在一个 redisServer 的结构体中…...

Transformer自然语言处理实战pdf阅读

一.第一章 欢迎来到transformer的世界 1.解码器-编码器框架 在Transformer出现之前&#xff0c;NLP的最新技术是LSTM等循环架构。这些架 构通过在神经网络连接使用反馈循环&#xff0c;允许信息从一步传播到另一 步&#xff0c;使其成为对文本等序列数据进行建模的理想选择。如…...

Python 高阶语法

前言&#xff1a; 我们通过上篇文章学习了Python的基础语法&#xff0c;接下来我们来学习Python的高阶语法 1.初识对象 在Python中我们可以做到和生活中那样&#xff0c;设计表格、生产表格、填写表格的组织形式的 面向对象包含 3 大主要特性&#xff1a;  封装  继承 …...

开始尝试从0写一个项目--前端(三)

器材管理板块 添加器材管理导航 src\views\home\Home.vue src\router\index.js src\views\equipment\Equipment.vue <template><div>hello!</div></template> 测试 搜索导航分页查询 src\views\equipment\Equipment.vue <template><div&…...

Visual stdio code 运行C项目环境搭建

参考 [1]VS Code 配置 C/C 编程运行环境&#xff08;保姆级教程&#xff09;_visual studio code c配置-CSDN博客 [2]最新VS code配置C/C环境(tasks.json, launch.json,c_cpp_properties.json)及运行多个文件、配置Cmake_vscode launch.json如何配置-CSDN博客 先装visual stdi…...

免杀笔记 -->API的整理Shellcode加密(过DeFender)

最近更新频率明显下降我懒&#xff0c;那么今天就来记录一下我们的一些常用的API的整理以及ShellCode的加密。 1.WinAPI整理 问我为什么要整理&#xff1f; 就是用起来的时候要左翻右翻 &#xff1a;&#xff1a; 烦死了 1.VirtualAlloc VirtualAlloc(NULL,sizeof(buf),MEM_…...

Stable Diffusion 使用详解(3)---- ControlNet

背景 炼丹师在AI绘画的过程中&#xff0c;由于Stable Diffusion的原理是水滴式的扩散作图原理&#xff0c;其实在前面也有提到&#xff0c;他的发挥是‘不稳定’的&#xff0c;因为你没有办法做到精确控制&#xff0c;只能说是大致符合你的预期。你不能总依赖抽卡固定随机数种…...

pythonGame-实现简单的贪食蛇游戏

通过python简单复现贪食蛇游戏。 使用到的库函数&#xff1a; import pygame import time import random 游戏源码&#xff1a; import pygame import time import randompygame.init()white (255, 255, 255) yellow (255, 255, 102) black (0, 0, 0) red (213, 50, 80…...

2024年软件系统与信息处理国际会议(ICSSIP 2024)即将召开!

2024年软件系统与信息处理国际会议&#xff08;ICSSIP 2024&#xff09;将于2024年10月25-27日在中国昆明举行。引领技术前沿&#xff0c;共谋创新未来。ICSSIP 2024将汇聚来自世界各地的专家学者&#xff0c;他们将在会上分享最新的研究成果、技术突破及实践经验。会议议题涵盖…...

使用vscode连接开发机进行python debug

什么是debug&#xff1f; 当你刚开始学习Python编程时&#xff0c;可能会遇到代码不按预期运行的情况。这时&#xff0c;你就需要用到“debug”了。简单来说&#xff0c;“debug”就是能再程序中设置中断点并支持一行一行地运行代码&#xff0c;观测程序中变量的变化&#xff…...

(家用)汽车充电桩项目总结分析

1. 项目选题背景 &#xff08;1&#xff09;社招&#xff1a;公司想做这个方向&#xff0c;先让学习测试一下&#xff0c;而且不做Web或者APP&#xff0c;以某一个模块或者某一个部分为主 &#xff08;2&#xff09;非社招&#xff1a;之前在学校做的一个学习的项目 2. 充电…...

JMeter接口测试:测试中奖概率!

介绍 Apache JMeter 是 Apache 组织基于 Java 开发的压力测试工具&#xff0c;用于对软件做压力测试。JMeter 最初被设计用于 Web 应用测试&#xff0c;但后来扩展到了其他测试领域&#xff0c;可用于测试静态和动态资源&#xff0c;如静态文件、Java 小服务程序、CGI 脚本、J…...

生成式人工智能之路,从马尔可夫链到生成对抗网络

人工智能&#xff08;Artificial intelligence&#xff0c;AI&#xff09;技术在过去几年中取得了显著进展&#xff0c;其中生成式AI&#xff08;Generative AI&#xff09;因其强大的内容生成能力而备受关注。生成式AI可以创建新的文本、图像、音频、视频、代码以及其他形式的…...

qt做的分页控件

介绍 qt做的分页控件 如何使用 创建 Pagination必须基于一个QWidget创建&#xff0c;否则会引发错误。 Pagination* pa new Pagination(QWidget*);设置总页数 Pagination需要设置一个总的页数&#xff0c;来初始化页码。 pa->SetTotalItem(count);设置可选的每页数量…...

MySQL with recursive 用法浅析

目录 写在前面 语句功能 with recursive 语法讲解 细节补充 “union all”语句 添加递归终止条件 写在前面 介绍“with recursive”用法的文章不少&#xff0c;但我都觉得讲的不够通俗&#xff0c;所以干脆自己写一篇。话不多说&#xff0c;进入正题。 语句功能 with r…...

ROS2常用命令集合

文章目录 指令帮助创建功能包功能包查找编译执行节点查看话题服务命令接口命令动作命令参数命令录制控制命令 指令帮助 ros2 --help # 帮助查看命令创建功能包 ros2 pkg create 包名 --build-type 构建类型 --dependencies 依赖列表 --node-name 可执行程序名称功能包查找 …...

VUE 子组件可以直接改变父组件的数据吗

子组件不可以直接改变父组件的数据。‌在Vue中&#xff0c;‌数据流是单向的&#xff0c;‌即父组件通过props向子组件传递数据&#xff0c;‌而子组件不能直接修改父组件的数据。‌这是为了维护数据流动的单向性和数据的可维护性。‌ 如果子组件需要修改父组件的数据&#xf…...

Redis 持久化详解

AOF 持久化 AOF持久化数据恢复相对RDB慢&#xff0c;文件也更大&#xff0c;但数据丢失的风险更小。 AOF 写入 将数据写入Redis内存后&#xff0c;将写数据的命令记录到AOP磁盘文件。 【结构】server.aof_buf 主线程写操作执行完之后&#xff0c;命令会先追加到 Redis 的 se…...

基于riscv64架构的Dayu800开发板的napi_demo开发介绍

itopen组织1、提供OpenHarmony优雅实用的小工具2、手把手适配riscv qemu linux的三方库移植3、未来计划riscv qemu ohos的三方库移植 小程序开发4、一切拥抱开源&#xff0c;拥抱国产化 一、环境准备工作 1.1 Ubuntu20.04环境配置 如果已经配置OpenHarmony的编译环境则…...

HAL STM32 SPI/ABZ/PWM方式读取MT6816磁编码器数据

HAL STM32 SPI/ABZ/PWM方式读取MT6816磁编码器数据 &#x1f4da;MT6816相关资料&#xff08;来自商家的相关资料&#xff09;&#xff1a; 资料&#xff1a;https://pan.baidu.com/s/1CAbdLBRi2dmL4D7cFve1XA?pwd8888 提取码&#xff1a;8888&#x1f4cd;驱动代码编写&…...

XCTF-web-easyupload

试了试php&#xff0c;php7&#xff0c;pht&#xff0c;phtml等&#xff0c;都没有用 尝试.user.ini 抓包修改将.user.ini修改为jpg图片 在上传一个123.jpg 用蚁剑连接&#xff0c;得到flag...

linux之kylin系统nginx的安装

一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源&#xff08;HTML/CSS/图片等&#xff09;&#xff0c;响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址&#xff0c;提高安全性 3.负载均衡服务器 支持多种策略分发流量…...

【WiFi帧结构】

文章目录 帧结构MAC头部管理帧 帧结构 Wi-Fi的帧分为三部分组成&#xff1a;MAC头部frame bodyFCS&#xff0c;其中MAC是固定格式的&#xff0c;frame body是可变长度。 MAC头部有frame control&#xff0c;duration&#xff0c;address1&#xff0c;address2&#xff0c;addre…...

解锁数据库简洁之道:FastAPI与SQLModel实战指南

在构建现代Web应用程序时&#xff0c;与数据库的交互无疑是核心环节。虽然传统的数据库操作方式&#xff08;如直接编写SQL语句与psycopg2交互&#xff09;赋予了我们精细的控制权&#xff0c;但在面对日益复杂的业务逻辑和快速迭代的需求时&#xff0c;这种方式的开发效率和可…...

在QWebEngineView上实现鼠标、触摸等事件捕获的解决方案

这个问题我看其他博主也写了&#xff0c;要么要会员、要么写的乱七八糟。这里我整理一下&#xff0c;把问题说清楚并且给出代码&#xff0c;拿去用就行&#xff0c;照着葫芦画瓢。 问题 在继承QWebEngineView后&#xff0c;重写mousePressEvent或event函数无法捕获鼠标按下事…...

基于 TAPD 进行项目管理

起因 自己写了个小工具&#xff0c;仓库用的Github。之前在用markdown进行需求管理&#xff0c;现在随着功能的增加&#xff0c;感觉有点难以管理了&#xff0c;所以用TAPD这个工具进行需求、Bug管理。 操作流程 注册 TAPD&#xff0c;需要提供一个企业名新建一个项目&#…...

20个超级好用的 CSS 动画库

分享 20 个最佳 CSS 动画库。 它们中的大多数将生成纯 CSS 代码&#xff0c;而不需要任何外部库。 1.Animate.css 一个开箱即用型的跨浏览器动画库&#xff0c;可供你在项目中使用。 2.Magic Animations CSS3 一组简单的动画&#xff0c;可以包含在你的网页或应用项目中。 3.An…...

jmeter聚合报告中参数详解

sample、average、min、max、90%line、95%line,99%line、Error错误率、吞吐量Thoughput、KB/sec每秒传输的数据量 sample&#xff08;样本数&#xff09; 表示测试中发送的请求数量&#xff0c;即测试执行了多少次请求。 单位&#xff0c;以个或者次数表示。 示例&#xff1a;…...

手机平板能效生态设计指令EU 2023/1670标准解读

手机平板能效生态设计指令EU 2023/1670标准解读 以下是针对欧盟《手机和平板电脑生态设计法规》(EU) 2023/1670 的核心解读&#xff0c;综合法规核心要求、最新修正及企业合规要点&#xff1a; 一、法规背景与目标 生效与强制时间 发布于2023年8月31日&#xff08;OJ公报&…...

若依登录用户名和密码加密

/*** 获取公钥&#xff1a;前端用来密码加密* return*/GetMapping("/getPublicKey")public RSAUtil.RSAKeyPair getPublicKey() {return RSAUtil.rsaKeyPair();}新建RSAUti.Java package com.ruoyi.common.utils;import org.apache.commons.codec.binary.Base64; im…...