详解 Redis 持久化之掌握 RDB ⽂件的格式,学习如何制作数据库镜像

news2024/11/17 7:33:17

本文带大家了解一下 Redis 数据一种持久化方式 RDB 的实现。包括 Redis 内存快照 RDB ⽂件的创建时机以及⽣成⽅法。可以让你掌握 RDB ⽂件的格式,学习如何制作数据库镜像。

RDB 创建的入口函数

Redis 创建 RDB 文件的函数有三个,分别是 rdbSave, rdbSaveBackground, rdbSaveToSlavesSockets 这三个函数。

rdbSave

rdbSave 是 Redis 在本地磁盘创建 RDB ⽂件的入口函数。它对应了 Redis 的 save 命令,会在 save 命令的实现函数 saveCommand 中被调用,这个命令是使用主线程执行的,会阻塞其他命令的执行。rdbSave 函数最终会调用 rdbSaveRio 函数来实际创建RDB⽂件。

rdbSaveBackground

rdbSaveBackground 是 Redis 使⽤⼦进程方式在本地磁盘创建 RDB ⽂件的入口函数。它对应了 Redis 的 bgsave 命令,会在 bgsave 命令的实现函数 bgsaveCommand 中被调⽤。这个函数会调⽤ fork 创建 ⼀个⼦进程,让⼦进程调用 rdbSave 函数来创建 RDB ⽂件,而主线程本⾝可以继续处理客户端请求。

int rdbSaveBackground(char *filename, rdbSaveInfo *rsi) {
    ...
    if ((childpid = fork()) == 0) {
        // 子进程执行方法
        ...
        // 调用 rdbSave 创建 RDB 文件
        retval = rdbSave(filename,rsi);
        ...
        // 子进程退出
        exitFromChild((retval == C_OK) ? 0 : 1);
    } else {
        /* Parent */
        // 父进程也就是主线程执行方法
        ...
        return C_OK;
    }
    return C_OK; /* unreached */
}

rdbSaveToSlavesSockets

rdbSaveToSlavesSockets 函数是 Redis 采用不落盘方式传输 RDB 文件进行主从复制时,创建 RDB文件的入口函数。

与 rdbSaveBackground 函数类似,rdbSaveToSlavesSockets 也是通过创建子进程,让子进程创建 RDB 文件。与 rdbSaveBackground 不同的是,rdbSaveToSlavesSockets 是通过网络以字节流的形式直接发送 RDB 文件的二进制文件数据给从节点。

RDB 创建时机

从上面的分析中我们知道 RDB 文件创建的三个时机分别是执行 save 命令、执行 bgsave 命令和进行主从复制。除了这几个时机还有哪些地方会触发 RDB 文件的创建呢?接下来我们将分析一下其他的创建时机。

rdbSave

通过查找 rdbSave 函数的调用,我们发现在 db.c 文件中的 flushallCommand 函数和 server.c 文件中的 prepareForShutdown 函数会调用 rdbSave 函数,也就是说在 Redis 执行 flushall 命令或 Redis 正常关闭时会创建 RDB 文件。

rdbSaveBackground

通过查找rdbSaveBackground 函数的调用,我们发现在 replication.c 中的 startBgsaveForReplication 函数和 server.c 文件中的 serverCron 函数会调用 rdbSaveBackground 函数,也就是说在主从复制以及定时任务按周期会调用 rdbSaveBackground 来创建 RDB 文件。

serverCron 函数会在下面两种情况下调用 rdbSaveBackground 生成 RDB 文件。

  • 满足配置的定时生成 RDB 文件的配置时。

  • bgsave 因为 AOF 重写导致 bgsave 被迫推迟时。

可见 RDB 文件只是周期性的保存某一时刻的数据。

rdbSaveToSlavesSockets

过查找rdbSaveToSlavesSockets 函数的调用,我们发现只有在 replication.c 中的 startBgsaveForReplication 函数会被调用,而 startBgsaveForReplication 函数被 replication.c ⽂件中的 syncCommand 函数和 replicationCron 函数调⽤,也就是说 Redis 执行主从复制命令以及周期性检测主从复制状态时会触发 RDB ⽣成。为了让从节点能够识别⽤来同步数据的 RDB 内容,rdbSaveToSlavesSockets 函数调⽤ rdbSaveRioWithEOFMark 函数在 RDB ⼆进制数据的前后加上了标识字符串,我们来看下代码:

#define RDB_EOF_MARK_SIZE 40

int rdbSaveRioWithEOFMark(rio *rdb, int *error, rdbSaveInfo *rsi) {
    char eofmark[RDB_EOF_MARK_SIZE];
    // 生成随机成 40 字节的 16 进制字符串,保存在 eofmark 中
    getRandomHexChars(eofmark,RDB_EOF_MARK_SIZE);
    if (error) *error = 0;
    // 写入 $EOF:
    if (rioWrite(rdb,"$EOF:",5) == 0) goto werr;
    // 写入 eofmark
    if (rioWrite(rdb,eofmark,RDB_EOF_MARK_SIZE) == 0) goto werr;
    // 写入 \r\n
    if (rioWrite(rdb,"\r\n",2) == 0) goto werr;
    // 写入 rdb 中的数据
    if (rdbSaveRio(rdb,error,RDB_SAVE_NONE,rsi) == C_ERR) goto werr;
    // 再次写入 eofmark
    if (rioWrite(rdb,eofmark,RDB_EOF_MARK_SIZE) == 0) goto werr;
    return C_OK;

werr: /* Write error. */
    /* Set 'error' only if not already set by rdbSaveRio() call. */
    if (error && *error == 0) *error = errno;
    return C_ERR;
}

新增的标识字符串如下图所示:

好了到这里我们找到了所有 RDB 创建的时机,下面这张图展示了函数的调用关系。

RDB 文件

一个 RDB 文件是主要由三部分组成的。

  • 文件头: 这部分内容保存了Redis 的魔数、RDB 版本、Redis 版本、RDB ⽂件创建时间、键值对占⽤的内存大小等信息。

  • 文件数据: 这部分保存了 Redis 数据库实际的所有键值对。

  • 文件尾: 这部分保存了 RDB ⽂件的结束标识符,以及整个⽂件的校验值。用于校验文件是否被篡改。 RDB 文件组成如下图所示:

​真正创建 RDB 文件的函数是 rdbSaveRio,下面我们通过 rdbSaveRio 函数分别看文件的这三部分具体实现。

文件头

rdbSaveRio 首先会将魔数写入 RDB文件,当在 RDB ⽂件头中写⼊魔数后,rdbSaveRio 函数紧接着会调⽤ rdbSaveInfoAuxFields 函数将和 Redis server 相关的⼀些属性信息写⼊ RDB ⽂件头。

...
    // 生成魔数 magic
    snprintf(magic,sizeof(magic),"REDIS%04d",RDB_VERSION);
    // 将魔数写到 RDB 中
    if (rdbWriteRaw(rdb,magic,9) == -1) goto werr;
    // 写入属性信息
    if (rdbSaveInfoAuxFields(rdb,flags,rsi) == -1) goto werr;
    if (rdbSaveModulesAux(rdb, REDISMODULE_AUX_BEFORE_RDB) == -1) goto werr;
...

文件数据

Redis Server 中会有多个数据库,rdbSaveRio 会遍历所有的数据库,并将里面的数据写入到 RDB 文件中。我们看一下代码实现:

...
    for (j = 0; j < server.dbnum; j++) {
        ...
        // 写入 SELECTDB 操作符
        if (rdbSaveType(rdb,RDB_OPCODE_SELECTDB) == -1) goto werr;
        // 写入数据库编号
        if (rdbSaveLen(rdb,j) == -1) goto werr;
        uint64_t db_size, expires_size;
        // 获取全局哈希表大小
        db_size = dictSize(db->dict);
        // 获取过期键哈希表大小
        expires_size = dictSize(db->expires);
        // 写入 RESIZEDB 操作符
        if (rdbSaveType(rdb,RDB_OPCODE_RESIZEDB) == -1) goto werr;
        // 写入全局哈希表大小
        if (rdbSaveLen(rdb,db_size) == -1) goto werr;
        // 写入过期键哈希表大小
        if (rdbSaveLen(rdb,expires_size) == -1) goto werr;
        // 遍历所有的键值对
        while((de = dictNext(di)) != NULL) {
            // 获取键
            sds keystr = dictGetKey(de);
            // 获取值对象
            robj key, *o = dictGetVal(de);
            long long expire;
            // 将 key 从 sds 类型转换为 robj
            initStaticStringObject(key,keystr);
            // 获取键的过期时间
            expire = getExpire(db,&key);
            // 将键、值以及过期时间写入 RDB 文件
            if (rdbSaveKeyValuePair(rdb,&key,o,expire) == -1) goto werr;

            /* When this RDB is produced as part of an AOF rewrite, move
             * accumulated diff from parent to child while rewriting in
             * order to have a smaller final write. */
            if (flags & RDB_SAVE_AOF_PREAMBLE &&
                rdb->processed_bytes > processed+AOF_READ_DIFF_INTERVAL_BYTES)
            {
                processed = rdb->processed_bytes;
                aofReadDiffFromParent();
            }
        }
        dictReleaseIterator(di);
        ...
    }
...

通过上面的代码我们可以看到,rdbSaveRio 函数会先将 SELECTDB 操作码和对应的数据库编号写⼊ RDB ⽂件中,这样方便解析时知道下面的数据是哪个数据库的。然后 rdbSaveRio 函数会写⼊ RESIZEDB 操作码,⽤来标识全局哈希表和过期 key 哈希表中键值对数量。最后 rdbSaveRio 函数会遍历当前数据库的所有键值对,把键、值以及过期时间写入 RDB 文件中。到这文件数据就写入完成了。

文件尾

写入文件尾的操作比较简单,主要写入两部分内容,一个是文件结束的操作码标识,另一个是校验码。下面我们看一下代码中是如何实现的:

    ...
    // 写入结束操作码
    if (rdbSaveType(rdb,RDB_OPCODE_EOF) == -1) goto werr;

    /* CRC64 checksum. It will be zero if checksum computation is disabled, the
     * loading code skips the check in this case. */
    cksum = rdb->cksum;
    memrev64ifbe(&cksum);
    // 写入校验码
    if (rioWrite(rdb,&cksum,8) == 0) goto werr;
    ...

好了,到这我们就分析完 Redis 中 RDB 文件的创建过程。

小结

本文主要介绍了内存快照文件 RDB 的三个入口函数以及创建 RDB 文件的时机还有 RDB 文件的生成过程。RDB 文件保存了 Redis 某一时刻所有的键值对,以及这些键值对的类型、大小、过期时间等信息。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/70671.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

知识图谱-KGE-语义匹配-双线性模型-2017:ANALOGY

【paper】 Analogical Inference for Multi-relational Embeddings【简介】 本文是卡耐基梅隆大学的中国学者发表在 ICML 2017 上的工作&#xff0c;提出了 ANALOGY 模型&#xff0c;用于建模实体和关系的推理属性。这个模型应当也算是双线性模型中比较经典的一个了&#xff0c…

Erueka基本使用

SpringCloud Erueka基本使用 Erueka是微服务架构中&#xff0c;可以作为注册中心的技术实现&#xff0c;如下图所示 服务提供者&#xff1a;一次业务中&#xff0c;暴露接口给其它微服务调用&#xff0c;被其它微服务调用的服务。&#xff08;提供接口给其它微服务&#xff09…

一个超好看的音乐网站设计与实现(HTML+CSS)

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

vue+vite的创建

1、创建vue3项目 yarn create vite效果&#xff1a; yarn create v1.22.19 [1/4] Resolving packages... [2/4] Fetching packages... [3/4] Linking dependencies... [4/4] Building fresh packages...success Installed "create-vite3.2.1" with binaries:- crea…

接口管理测试繁琐复杂?何不试试这个神器

一、前言 作为一名测试从业者&#xff0c;深刻的明白接口测试在项目过程中是多么重要的一个环节。通过页面进行的UI测试会因为界面不稳定而导致用例维护非常困难。另外&#xff0c;在检查系统的安全性、稳定性上面也是尤为重要的环节&#xff0c;这些也是无法通过前端测试的&a…

Redis - Windows下载与安装

1.获取Redis在windows下的安装包 Windows版下载地址&#xff1a;https://github.com/microsoftarchive/redis/releases 选择Redis-x64-*.zip 2.解压zip文件与配置 2.1 选取目录 选取一个目录作为解压目录&#xff0c;这个目录就是你Redis程序所在位置&#xff0c;尽量找一…

【软件测试】师傅给我的测试新手“真理“宝典......

目录&#xff1a;导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09;前言 熟悉你所测试的软件…

java学习day59(乐友商城)Vue入门

0.前言 前几天我们已经对后端的技术栈有了初步的了解、并且已经搭建了整个后端微服务的平台。接下来要做的事情就是功能开发了。但是没有前端页面&#xff0c;我们肯定无从下手&#xff0c;因此今天我们就要来了解一下前端的一些技术&#xff0c;完成前端页面搭建。 先聊一下…

【Redis】Redis实现分布式锁解析与应用(Redis专栏启动)

&#x1f4eb;作者简介&#xff1a;小明java问道之路&#xff0c;专注于研究 Java/ Liunx内核/ C及汇编/计算机底层原理/源码&#xff0c;就职于大型金融公司后端高级工程师&#xff0c;擅长交易领域的高安全/可用/并发/性能的架构设计与演进、系统优化与稳定性建设。 &#x1…

java版商城+Spring Cloud+SpringBoot+mybatis+uniapp b2b2c o2o 多商家入驻商城 直播带货商城 电子商务

一个好的SpringCloudSpringBoot b2b2c 电子商务平台涉及哪些技术、运营方案&#xff1f;以下是我结合公司的产品做的总结&#xff0c;希望可以帮助到大家&#xff01; 搜索体验小程序&#xff1a;海哇 1. 涉及平台 平台管理、商家端&#xff08;PC端、手机端&#xff09;、买…

助农销售平台毕业设计,农产品销售管理系统设计与实现,毕业设计怎么写论文源码开题报告需求分析怎么做

项目背景和意义 目的&#xff1a;本课题主要目标是设计并能够实现一个基于web网页的多用户商城系统&#xff0c;整个网站项目使用了B/S架构&#xff0c;基于java的springboot框架下开发&#xff1b;用户通过登录网站&#xff0c;查询商品&#xff0c;购买商品&#xff0c;下单&…

ElasticSearch-7.17支持两种客户端连接方式(RestHighLevelClient 和Elasticsearch Java API)

学习es时发现了一个大问题&#xff0c;学习的版本为7.8.0&#xff08;尚硅谷yyds&#xff09;&#xff0c;自己使用的是7.17.8&#xff0c;但是最新的版本已经是8.5X了&#xff08;心累&#xff0c;怎么升级这么快&#xff09;。 因为目前用的还是jdk1.8&#xff0c;所以就按照…

Blazor组件自做十二 : Blazor Pdf Reader PDF阅读器 组件

原文链接 [https://www.cnblogs.com/densen2014/p/16954812.html] Blazor Pdf Reader PDF阅读器 组件 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IChv1OZ2-1670428567016)(https://img.shields.io/nuget/v/BootstrapBlazor.PdfReader.svg?styl…

【YOLOX 论文+源码解读】YOLOX: Exceeding YOLO Series in 2021

目录前言一、整体网络架构二、改进点1.1、解耦头1.2、Anchor Free1.3、SimOTA三、源码解析3.1、Backbone3.2、Neck3.3、head3.4、预测&#xff1a;decode_outputs3.5、训练&#xff1a;get_losses3.5.1、准备工作&#xff1a;get_output_and_grid3.5.2、get_losses函数&#xf…

神操作!竟然有人用Python在Excel中画画,女神看了直呼震惊

十字绣大家都知道吧&#xff0c;今天咱们来玩个电子版的十字绣。 用 Python 读取图片的像素值&#xff0c;然后输出到 Excel 表格中&#xff0c;最终形成一幅像素画&#xff0c;也就是电子版的十字绣了。 准备 既然要读取图片&#xff0c;那就需要用到 Pillow 库&#xff0c…

做更真实的实验!艾美捷Caspase-1活性分析试剂盒方案

FLICA 是通过检测来评估细胞死亡的有力方法体外凋亡。免疫化学技术&#xff08;ICT&#xff09;FLICA 荧光标记的抑制剂与活性半胱天冬酶共价结合的CAspa酶1,2。FLICA测量细胞内的凋亡过程&#xff0c;而不是副作用&#xff0c;如磷脂酰丝氨酸的翻转&#xff0c;并消除假阳性的…

iOS视频编码实战VideoToolbox

需求 iOS中编码视频数据,一般情况而言一个项目仅需要一个编码器,不过有时特殊需求可能需要两个编码器同时工作.本例中实现了编码器类.仅通过指定不同编码器的枚举值就可以快速生成需要的编码器,且支持两个编码器一起工作. 实现原理: iOS中利用VideoToolBox框架完成视频硬编码…

TS201的Flag输出状态控制LED亮灭原理和例程(含参考代码)

目的&#xff1a; 理解FLAG可编程作为输入输出引脚&#xff0c;并且能够利用按键进行相应FLAG&#xff08;FLAG0,FLAG1&#xff09;标志的输入来相应的FLAG标志(FLAG2,FLAG3)输出来控制与之相连的LED。掌握外部中断和定时器中断的设置以及其响应过程&#xff0c;理解外部硬件可…

hevc帧内planer预测模式和角度预测模式

帧内planer预测模式 planer预测模式适用于纹理相对平缓的图像区域&#xff0c;对于各个编码宏块而言&#xff0c;它不但能保持图像宏块边界良好的连续性。而且可以利用平面梯度信号随像素值的变化趋势而变化&#xff0c;在Planer预测模式下&#xff0c;可以将预测像素Px,y 看作…

企业应收账款管理存在的问题及对策

应收账款也就是信用交易&#xff0c;企业应收账款的产生是企业采取信用销售方式的必然结果。 现如今信用交易已经成为企业提高竞争力、扩大销售的必要手段&#xff0c;它充分挖掘和利用了企业的现有生产能力&#xff0c;扩大了销售量&#xff0c;增加了产品的市场份额&#xf…