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

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
简介: 【Redis源码】轻松看懂rdb文件(四)

前言:

该篇内容为我对redis的学习记录,欢迎指正批评。

一.数据存储格式:

二.查看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  73
          R   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  c2
          s   -   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  6c
        371 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  31
          e 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  30
          7   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  64
          d   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  73
          e   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  01
          e   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  fa
          a  \0 005   t   e   s   t   1 300  \a 377  \f   s 242  \0 372
00000b0    73  ff  b1                                                    
          s 377 261                                                    
00000b3

三.源码解析及文件解析

对应源代码:

intrdbSaveRio(rio *rdb, int *error, int flags, rdbSaveInfo *rsi) {
   dictIterator *di = NULL;
   dictEntry *de;
   char magic[10];
   int j;
   longlong 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,默认是16
       redisDb *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对应251
       if (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);
           longlong 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;
}

//辅助字符串方法
intrdbSaveInfoAuxFields(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;
   
   //写入当前实例replid
   if (rdbSaveAuxFieldStrStr(rdb,"repl-id",server.replid) == -1) return-1;
   
   //当前实例复制的偏移量
   if (rdbSaveAuxFieldStrInt(rdb,"repl-offset",server.master_repl_offset) == -1) return-1;
   return1;
}


intrdbSaveAuxField(rio *rdb, void *key, size_t keylen, void *val, size_t vallen) {
   if (rdbSaveType(rdb,RDB_OPCODE_AUX) == -1) return-1;   //写入fa
   if (rdbSaveRawString(rdb,key,keylen) == -1) return-1;  //写入key
   if (rdbSaveRawString(rdb,val,vallen) == -1) return-1;  //写入val
   return1;
}

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**

常量 值10进制 值16进制 备注
RDB_OPCODE_AUX 250 FA aux其实就是分割符fa,
RDB_OPCODE_RESIZEDB 251 FB DB size
RDB_OPCODE_EXPIRETIME_MS 252 FC 过期时间非-1时写入
RDB_OPCODE_EXPIRETIME 253 FD 用于加载和检测rdb时使用
RDB_OPCODE_SELECTDB 254 FE 选择数据库
RDB_OPCODE_EOF 255 FF 结尾

从opcode列表我们可以看出从fe开始,fe对应opcode的RDB_OPCODE_SELECTDB。

写入RDB_OPCODE_SELECTDB调用rdbSaveLen,rdbSaveLen函数会计算写入的长度。由于写入比较值小,则是1个字节;

rdb.c 保存键值方法

intrdbSaveKeyValuePair(rio *rdb, robj *key, robj *val,
                       longlong expiretime, longlong now)

{
   /* 保存过期时间 */
   if (expiretime != -1) {
       /* If this key is already expired skip it */
       if (expiretime < now) return0;
       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;
   return1;
}

rdbSaveObjectType保存类型

类型 编码 备注
OBJ_STRING - RDB_TYPE_STRING 0 字符串
OBJ_LIST OBJ_ENCODING_QUICKLIST RDB_TYPE_LIST_QUICKLIST 14 list 双向链表
OBJ_SET OBJ_ENCODING_INTSET RDB_TYPE_SET_INTSET 11 整型集合
OBJ_SET OBJ_ENCODING_HT RDB_TYPE_SET 2 hash集合
OBJ_ZSET OBJ_ENCODING_ZIPLIST RDB_TYPE_ZSET_ZIPLIST 12 zset 压缩表
OBJ_ZSET OBJ_ENCODING_SKIPLIST RDB_TYPE_ZSET_2 5 zset 跳跃表
OBJ_HASH OBJ_ENCODING_ZIPLIST RDB_TYPE_HASH_ZIPLIST 13 hash 压缩表
OBJ_HASH OBJ_ENCODING_HT RDB_TYPE_HASH 4 hash 表
OBJ_MODULE - RDB_TYPE_MODULE_2 7 模块

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

写入name这个键的时候因为没有写过期时间所以没有过期时间标识和过期时间,然后写入时是用rdbSaveStringObject,

rdbSaveStringObject由于不是整形编码,则调用了rdbSaveRawString函数。rdbSaveRawString就会写入一个键值长度。

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
7天前
|
存储 NoSQL Redis
Redis 持久化揭秘:选择 RDB、AOF 还是混合持久化?
Redis 是一个内存数据库,意味着它主要将数据存储在内存中,从而能够提供极高的性能。然而,作为内存数据库,Redis 默认情况下的数据不会永久保存。为了确保数据在重启或故障后能够恢复,Redis 提供了几种 **持久化机制**。这些机制允许 Redis 将内存中的数据保存到硬盘上,从而实现数据持久化。
61 22
Redis 持久化揭秘:选择 RDB、AOF 还是混合持久化?
|
1月前
|
存储 监控 NoSQL
【赵渝强老师】Redis的RDB数据持久化
Redis 是内存数据库,提供数据持久化功能以防止服务器进程退出导致数据丢失。Redis 支持 RDB 和 AOF 两种持久化方式,其中 RDB 是默认的持久化方式。RDB 通过在指定时间间隔内将内存中的数据快照写入磁盘,确保数据的安全性和恢复能力。RDB 持久化机制包括创建子进程、将数据写入临时文件并替换旧文件等步骤。优点包括适合大规模数据恢复和低数据完整性要求的场景,但也有数据完整性和一致性较低及备份时占用内存的缺点。
|
2月前
|
存储 缓存 NoSQL
大数据-45 Redis 持久化概念 RDB AOF机制 持久化原因和对比
大数据-45 Redis 持久化概念 RDB AOF机制 持久化原因和对比
53 2
大数据-45 Redis 持久化概念 RDB AOF机制 持久化原因和对比
|
3月前
|
存储 缓存 NoSQL
Redis中的rdb和aof
本文深入探讨了Redis的持久化机制,包括RDB和AOF两种方式。详细解释了RDB的工作原理、优势和劣势,以及AOF的实现原理、配置选项、文件重写机制和三种数据同步方式,还介绍了AOF文件修复工具redis-check-aof的使用,并通过实例展示了如何开启和配置AOF持久化方式。
Redis中的rdb和aof
|
2月前
|
缓存 NoSQL Ubuntu
大数据-39 Redis 高并发分布式缓存 Ubuntu源码编译安装 云服务器 启动并测试 redis-server redis-cli
大数据-39 Redis 高并发分布式缓存 Ubuntu源码编译安装 云服务器 启动并测试 redis-server redis-cli
65 3
|
2月前
|
设计模式 NoSQL 网络协议
大数据-48 Redis 通信协议原理RESP 事件处理机制原理 文件事件 时间事件 Reactor多路复用
大数据-48 Redis 通信协议原理RESP 事件处理机制原理 文件事件 时间事件 Reactor多路复用
44 2
|
2月前
|
存储 缓存 NoSQL
大数据-46 Redis 持久化 RDB AOF 配置参数 混合模式 具体原理 触发方式 优点与缺点
大数据-46 Redis 持久化 RDB AOF 配置参数 混合模式 具体原理 触发方式 优点与缺点
72 1
|
3月前
|
存储 NoSQL Redis
SpringCloud基础7——Redis分布式缓存,RDB,AOF持久化+主从+哨兵+分片集群
Redis持久化、RDB和AOF方案、Redis主从集群、哨兵、分片集群、散列插槽、自动手动故障转移
SpringCloud基础7——Redis分布式缓存,RDB,AOF持久化+主从+哨兵+分片集群
|
3月前
|
存储 NoSQL Redis
Redis的RDB快照:保障数据持久性的关键机制
Redis的RDB快照:保障数据持久性的关键机制
77 0
|
4月前
|
NoSQL Redis
【Azure Redis】Redis导入备份文件(RDB)失败的原因
【Azure Redis】Redis导入备份文件(RDB)失败的原因