【Redis源码】轻松看懂 rdb 文件

news/2025/3/11 6:25:51/文章来源:https://www.cnblogs.com/o-O-oO/p/18671927

一、数据存储格式

二、查看rdb文件

查看文件16进制编码

#od -A x -t x1c -v dump.rdb

RDB文件格式如下:

0000000    52  45  44  49  53  30  30  30  38  fa  09  72  65  64  69  73R   E   D   I   S   0   0   0   8 372  \t   r   e   d   i   s
0000010    2d  76  65  72  05  34  2e  30  2e  30  fa  0a  72  65  64  69-   v   e   r 005   4   .   0   .   0 372  \n   r   e   d   i
0000020    73  2d  62  69  74  73  c0  40  fa  05  63  74  69  6d  65  c2s   -   b   i   t   s 300   @ 372 005   c   t   i   m   e   ¦
0000030    a6  be  1d  5e  fa  08  75  73  65  64  2d  6d  65  6d  c2  80** 276 035   ^ 372  \b   u   s   e   d   -   m   e   m 302 200
0000040    f9  0e  00  fa  0c  61  6f  66  2d  70  72  65  61  6d  62  6c371 016  \0 372  \f   a   o   f   -   p   r   e   a   m   b   l
0000050    65  c0  00  fa  07  72  65  70  6c  2d  69  64  28  66  36  31e 300  \0 372  \a   r   e   p   l   -   i   d   (   f   6   1
0000060    37  64  35  62  39  62  38  65  32  61  31  63  64  66  39  307   d   5   b   9   b   8   e   2   a   1   c   d   f   9   0
0000070    64  33  35  64  33  32  35  34  32  38  36  62  36  30  38  64d   3   5   d   3   2   5   4   2   8   6   b   6   0   8   d
0000080    65  39  66  30  39  fa  0b  72  65  70  6c  2d  6f  66  66  73e   9   f   0   9 372  \v   r   e   p   l   -   o   f   f   s
0000090    65  74  c0  00  fe  00  fb  02  00  00  04  6e  61  6d  65  01e   t 300  \0 376  \0 373 002  \0  \0 004   n   a   m   e 001
00000a0    61  00  05  74  65  73  74  31  c0  07  ff  0c  73  a2  00  faa  \0 005   t   e   s   t   1 300  \a 377  \f   s 242  \0 372
00000b0    73  ff  b1                                                    s 377 261                                                    
00000b3

三、源码解析及文件解析

对应源代码:

int rdbSaveRio(rio *rdb, int *error, int flags, rdbSaveInfo *rsi) {dictIterator *di = NULL;dictEntry *de;char magic[10];int j;long long now = mstime();uint64_t cksum;size_t processed = 0;if (server.rdb_checksum)rdb->update_cksum = rioGenericUpdateChecksum;snprintf(magic,sizeof(magic),"REDIS%04d",RDB_VERSION);    //魔法字符串if (rdbWriteRaw(rdb,magic,9) == -1) goto werr;            //写入9个字节if (rdbSaveInfoAuxFields(rdb,flags,rsi) == -1) goto werr; //辅助字符串for (j = 0; j < server.dbnum; j++) { //遍历db,默认是16redisDb *db = server.db+j;dict *d = db->dict;if (dictSize(d) == 0) continue;di = dictGetSafeIterator(d);if (!di) return C_ERR;/* 写入 SELECT DB opcode */if (rdbSaveType(rdb,RDB_OPCODE_SELECTDB) == -1) goto werr; if (rdbSaveLen(rdb,j) == -1) goto werr;/* Write the RESIZE DB opcode. We trim the size to UINT32_MAX, which* is currently the largest type we are able to represent in RDB sizes.* However this does not limit the actual size of the DB to load since* these sizes are just hints to resize the hash tables. */uint32_t db_size, expires_size;db_size = (dictSize(db->dict) <= UINT32_MAX) ?dictSize(db->dict) :UINT32_MAX;expires_size = (dictSize(db->expires) <= UINT32_MAX) ?dictSize(db->expires) :UINT32_MAX;if (rdbSaveType(rdb,RDB_OPCODE_RESIZEDB) == -1) goto werr;  //写入数据库 RDB_OPCODE_RESIZEDB对应251if (rdbSaveLen(rdb,db_size) == -1) goto werr;if (rdbSaveLen(rdb,expires_size) == -1) goto werr;/* Iterate this DB writing every entry */while((de = dictNext(di)) != NULL) {sds keystr = dictGetKey(de);robj key, *o = dictGetVal(de);long long expire;initStaticStringObject(key,keystr);expire = getExpire(db,&key);if (rdbSaveKeyValuePair(rdb,&key,o,expire,now) == -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);}di = NULL; /* So that we don't release it again on error. *//* EOF opcode */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;  //写入8位校验和return C_OK;werr:if (error) *error = errno;if (di) dictReleaseIterator(di);return C_ERR;
}//辅助字符串方法
int rdbSaveInfoAuxFields(rio *rdb, int flags, rdbSaveInfo *rsi) {int redis_bits = (sizeof(void*) == 8) ? 64 : 32;int aof_preamble = (flags & RDB_SAVE_AOF_PREAMBLE) != 0;//写入版本if (rdbSaveAuxFieldStrStr(rdb,"redis-ver",REDIS_VERSION) == -1) return -1;  //写入redis(OS arch)if (rdbSaveAuxFieldStrInt(rdb,"redis-bits",redis_bits) == -1) return -1; //写入时间戳if (rdbSaveAuxFieldStrInt(rdb,"ctime",time(NULL)) == -1) return -1; //写入使用内存大小if (rdbSaveAuxFieldStrInt(rdb,"used-mem",zmalloc_used_memory()) == -1) return -1; /* Handle saving options that generate aux fields. */if (rsi) {//写入server.master选择的数据库if (rsi->repl_stream_db &&rdbSaveAuxFieldStrInt(rdb,"repl-stream-db",rsi->repl_stream_db) == -1){return -1;}}//写入是否开启混合模式if (rdbSaveAuxFieldStrInt(rdb,"aof-preamble",aof_preamble) == -1) return -1;//写入当前实例replidif (rdbSaveAuxFieldStrStr(rdb,"repl-id",server.replid) == -1) return -1;//当前实例复制的偏移量if (rdbSaveAuxFieldStrInt(rdb,"repl-offset",server.master_repl_offset) == -1) return -1;return 1;
}int rdbSaveAuxField(rio *rdb, void *key, size_t keylen, void *val, size_t vallen) {if (rdbSaveType(rdb,RDB_OPCODE_AUX) == -1) return -1;   //写入faif (rdbSaveRawString(rdb,key,keylen) == -1) return -1;  //写入keyif (rdbSaveRawString(rdb,val,vallen) == -1) return -1;  //写入valreturn 1;
}

magic字符串:

52 45 44 49 53 30 30 30 38 这9个字节对应 char magic[10]字符串的9个字节

AuxFields辅助字段字符串:

字段 备注
redis-ver 版本号
redis-bits 系统位数(OS arch)
ctime RDB创建文件时间
used-mem 使用内存大小
repl-stream-db server.master 选择的数据库
aof-preamble 是否开启混合模式
repl-id 当前实例replid
repl-offset 当前实例复制的偏移量

16进制中应该可以看到fa字样,通用字符串又是怎么识别呢。其实fa是一个分割符,fa后面的一个变量则是具体的key的长度;
如图所示:

RDB opcodes

从opcode列表我们可以看出从fe开始,fe对应opcode的RDB_OPCODE_SELECTDB。
写入RDB_OPCODE_SELECTDB调用rdbSaveLen,rdbSaveLen函数会计算写入的长度。由于写入比较值小,则是1个字节;

rdb.c 保存键值方法


int rdbSaveKeyValuePair(rio *rdb, robj *key, robj *val,long long expiretime, long long now)
{/* 保存过期时间 */if (expiretime != -1) {/* If this key is already expired skip it */if (expiretime < now) return 0;if (rdbSaveType(rdb,RDB_OPCODE_EXPIRETIME_MS) == -1) return -1;if (rdbSaveMillisecondTime(rdb,expiretime) == -1) return -1;}/* 保存 type, key, value */if (rdbSaveObjectType(rdb,val) == -1) return -1;if (rdbSaveStringObject(rdb,key) == -1) return -1;if (rdbSaveObject(rdb,val) == -1) return -1;return 1;
}

rdbSaveObjectType保存类型

根据以上的为写入所有编码。

写入name这个键的时候因为没有写过期时间所以没有过期时间标识和过期时间,然后写入时是用rdbSaveStringObject,
rdbSaveStringObject由于不是整形编码,则调用了rdbSaveRawString函数。rdbSaveRawString就会写入一个键值长度。

原创 赵禹 爱因诗贤

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【供应链管理系统】你了解供应链管理的五大系统(ERP、WMS、TMS、CRM和OMS)吗?

供应链管理听起来可能很复杂,但它其实是现代企业运营中不可或缺的一部分。 想象一下,从生产原材料到产品配送到客户手中,这整个过程是如何协调运作的。 每个环节需要无缝对接,确保产品的及时生产、运输和交付。 而要想高效地完成这些任务,企业就需要依赖一系列强大的系统工…

【C++安全】C++ 直接编写 Shellcode 和使用常量字符串

免责声明: 该公众号分享的安全工具和项目均来源于网络,仅供安全研究与学习之用,如用于其他用途,由使用者承担全部法律及连带责任,与工具作者和本公众号无关。一、基础知识1. PE 文件的基本结构和作用2. PE 文件加载流程3. 章节总结 二、编写 MessageBox Shellcode1. 开始2…

Easysearch Rollup 使用指南

背景 在现代数据驱动的世界中,时序数据的处理变得越来越重要。无论是监控系统、日志分析,还是物联网设备的数据收集,时序数据都占据了大量的存储空间。随着时间的推移,这些数据的存储成本和管理复杂度也在不断增加。 为了解决这一问题,Rollup 技术应运而生。本文将带你深入…

.NET 数据拷贝方案选择

应用中我们经常使用到数据的复制,在.NET中有多种方式可以实现复制数据或对象。选择哪种方式通、是浅拷贝还是深拷贝,取决于对象的复杂性、数据量以及具体需求场景。1. MemberwiseClone拷贝 浅拷贝 Object.MemberwiseClone 方法 (System) | Microsoft Learn,指针对对象执行非…

1.14 eclipse配置spring

今天完成了eclipse配置springboot eclipse本身并没有spring项目,需要在eclipse市场下载插件选择tool4安装安装完成等待eclipse加载,全部安装完成后即可创建spring项目

英语语法(标点符号:逗号和撇号)

结束句子的三种方法 认识逗号

深入浅出:Agent如何调用工具——从OpenAI Function Call到CrewAI框架

深入浅出:Agent如何调用工具——从OpenAI Function Call到CrewAI框架 嗨,大家好!作为一个喜欢折腾AI新技术的算法攻城狮,最近又学习了一些Agent工作流调用工具的文章,学完之后,我真的是“啊这”,一边感慨AI技术的强大,一边觉得自己打开了新世界的大门。于是,我决定写这…

在Ubantu中安装pycharm

1.下载pycharm linux版,我下载的是2022.3.3专业版 2. 更改host文件,输入: sudo gedit /etc/hosts在弹出的文件中的末尾加以下代码: 0.0.0.0 account.jetbrains.com3.激活pycharm: 将pycharm补丁jet-netfilter拷入ubantu中某一路径(注意是整个文件夹放进去,不要只放jar包…

docker-compose自动部署go项目全流程,本地到镜像仓库到服务器,踩坑笔记

声明:个人所学记录,有可以改进的地方希望不吝指教 Dockerfile # 使用golang官方镜像作为构建环境 FROM golang:1.23-alpine AS builder# 设置工作目录 WORKDIR /app# 设置环境变量镜像变量 ENV GO111MODULE=on ENV GOPROXY=https://goproxy.cn,direct# 复制go.mod 和 go.sum文…

docker部署d2l环境

编写dockerfile # 使用NVIDIA提供的CUDA基础镜像,包含CUDA 11.8.0和cuDNN 8,基于Ubuntu 22.04 FROM nvidia/cuda:11.8.0-cudnn8-devel-ubuntu22.04 # 设置维护者信息 MAINTAINER watcherprime <woma@126.com># 设置环境变量,包括时区、非交互式前端和PATH变量 ENV TZ=…

【TCP协议】TCP Keepalive 指南

1、什么是 TCP Keepalive?TCP Keepalive 是一种 TCP 协议内置的探测机制,用于检测长时间未活动的连接是否仍然存活。当启用了 Keepalive 后,TCP 会在连接空闲一定时间后,定期向对端发送探测包,如果未收到对端的响应,则会尝试多次探测,最终关闭连接。 用途: 检测并清理死…

《CPython Internals》阅读笔记:p151-p151

《CPython Internals》学习第 9天,p151-p1510 总结,总计 1 页。 一、技术总结 无。 二、英语总结(生词:1) 1.marshal (1)marshaling Marshalling or marshaling(US spelling) is the process of transforming the memory representation of an object into a data form su…