深入理解Redis—RDB持久化

Redis的RDB持久化功能将内存中的数据库状态保存到磁盘,通过SAVE或BGSAVE命令实现,其中BGSAVE在后台执行,避免服务器阻塞。RDB文件是一个压缩的二进制文件,用于在服务器启动时还原数据库状态。服务器通过配置的save选项自动执行BGSAVE,满足特定条件如900秒内有一次修改等。RDB文件结构包括REDIS标识、版本号、数据库信息和校验和等,不同类型的键值对有不同的存储方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Redis是一个键值对数据库服务器;

服务器的非空数据库以及其中的键值对统称为“数据库状态”;

Redis是内存数据库,为防止进程退出数据消失,必须将数据库状态持久化到磁盘文件中;

      Redis提供RDB持久化功能,可将内存中的数据库状态保存到磁盘中;

      RDB持久化既可以手动执行,也可以根据配置服务器的配置选项定期执行,该功能可以将某个时间点上的数据库状态保存到一个RDB文件中;

      RDB持久化功能生成的RDB文件一个经过压缩的二进制文件,服务器在启动时通过加载RDB文件还原数据库状态;

RDB文件的创建与载入

      SAVE命令、BGSAVE命令都可用于生成RDB文件;

      SAVE命令会阻塞服务器进程,直到RDB文件创建完毕为止,在服务器进程阻塞期间不能处理任何命令请求;

      BGSAVE命令会派生出一个子进程,由子进程在后台负责创建RDB文件,服务器进程继续处理命令请求;

      创建RDB文件的实际工作是底层的rdb.c/rdbSAVE函数,两个命令会以不同的方式调用该函数;(大致流程:①SAVE命令直接调用该函数进行RDB文件的生成;②BGSAVE命令,则先fork一个子进程,通过子进程调用该函数创建RDB文件,结束后向服务器进程发送信号,服务器进程阻塞处理该信号)

      RDB文件的载入工作是在Redis服务器启动时自动执行的,没有专门的命令(服务器在启动时检测到RDB文件时,便自动将其载入)

      注:AOF文件的更新频率通常比RDB文件的更新频率高,所以若服务器同时开启了AOF持久化功能,那么服务器会优先使用AOF文件来还原数据库状态;只有在AOF持久化处于关闭状态时,服务器才会使用RDB文件来还原数据库状态;

SAVE命令执行时的服务器状态

      执行SAVE命令的进程将会被阻塞从而无法处理任何命令请求;

BGSAVE命令执行时的服务器状态

      BGSAVE命令执行期间,服务器处理SAVE、BGSAVE、BGREWRITAOF三个命令的方式会与平时不同;

  • BGSAVE命令执行期间,客户端发送的SAVE命令会被服务器拒绝(服务器进制SAVE命令和BGSAVE命令同时执行是为了避免父进程和子进程同时执行两个rdbSave的调用,防止产生竞争条件)
  • BGSAVE命令执行期间,客户端发送的BGSAVE命令会被服务器拒绝,同时执行两个BGSAVE命令也会产生竞争条件;
  • BGREWRITEAOF和BGSAVE两个命令不能同时执行:
    1. 若BGSAVE命令正在执行,则客户端发送的BGREWRITEAOF命令会被延迟到BGSAVE命令执行完毕之后再执行;
    2. 若BGREWRITEAOF命令正在执行,则客户端发送的BGSAVE命令会被服务器拒绝

BGSAVE与BGREWRITEAOF命令实际工作虽然都由子进程执行,但并无冲突,不同是执行只是出于性能方面的考虑;

RDB文件载入时的服务器状态

     

      服务器载入RDB文件期间,会一直出于阻塞状态,直到载入工作完成;

自动间隔性保存

      BGSAVE命令可以在不阻塞服务器进程的情况下执行,因此Redis允许用户通过设置服务器配置的save选项,让服务器每隔一段时间自动执行一次BGSAVE命令;

      save选项设置了多个保存条件,但只要其中任意一个被满足,服务器就会执行BGSAVE命令,一般情况下,服务器配置默认的save选项:

  • save  900  1                ——服务器在900秒内对数据库进行了一次修改
  • save  300  10              ——服务器在300秒内对数据库进行了十次修改
  • save  60   10000         ——服务器在60秒内对数据库进行了一万次修改

Notes:save选项都保存在服务器状态(redisServer)中的saveparams数组中,每个数组元素记录了一个save选项设置的值;

设置自动保存条件

      Redis服务器启动时,用户可通过指定配置文件  或者  传入启动参数的方式设置save选项;

      若用户没有手动设置该选项值,则服务器使用配置文件中默认的自动保存条件;

dirty计数器和lastsave属性

      服务器状态维护一个dirty计数器以及lastsave属性:

  • dirty计数器记录了距离上一次成功执行SAVE或者BGSAVE命令后,服务器对数据库状态进行了多少次修改(增删改操作);
  • lastsave属性是一个UNIX时间戳,记录了服务器上一次成功执行SAVE或者BGSAVE命令的时间;

检查保存条件是否满足

      Redis的服务器周期性函数serverCron默认每隔100ms就会执行一次,该函数用于对正在运行的服务器进行维护,其中一项工作就是检查save选项所设置的保存条件是否已经满足;

      程序会遍历并检查saveparams数组中的所有保存条件,只要有任意一个条件被满足,就会执行BGSAVE命令;

RDB文件结构

 

     

注:“常量”使用大写标识,“变量、数据”使用小写标识;

RDB文件最开头是REDIS部分,该部分长度为5字节,保存着“REDIS”五个字符。

载入RDB文件时,通过校验文件头的这五个字来确认载入的是否为RDB文件;

db_version长度为4字节,其值是一个字符串表示的整数,该整数记录了RDB文件的版本号;

databases部分包含着0个或者任意多个数据库,以及数据库中的键值对数据;

EOF常量的长度为1字节,标识RDB文件的正文内容结束;

check_sum为8字节的无符号整数,保存着一个校验和;该校验和是程序通过对前四部分内容的计算得出的;服务器载入RDB文件时,会通过计算前四部分得出的校验和与文件尾部的check_sum值比较来检查文件是否有损坏或出错的情况;

databases部分

      RDB文件的databases部分可以保存任意多个非空数据库;

 

      每个非空数据库在RDB文件中都可以保存为SELECTDB、db_number、key_value_paris三个部分

 

  • SELECTDB常量长度为1字节,当读入程序遇到该值时,将知晓即将读入一个数据库号码;
  • db_number保存着一个数据库号码,根据号码大小不同,该部分长度可以为1字节、2字节、5字节;(当程序读入db_number部分之后,服务器会自动调用SELECT命令,根据读入的数据库号码进行数据库切换,使得之后读入的键值对可以载入到正确的数据库中)
  • key_value_pairs部分保存了数据库中所有键值对数据,若键值对带有过期时间,则过期时间也会和键值对保存在一起;

 

key_value_pairs部分

      RDB文件中的每个key_value_pairs部分都保存了一个或以上数量的键值对,若键值对带有过期时间,则过期时间也会保存在内;

      不带过期时间的键值对在RDB文件中由TYPE、key、value三部分组成:

 

TYPE记录了value的类型,长度为1字节;TYPE的一些列常量代表了对象类型或底层编码,当服务器读入RDB文件中的键值对数据时,程序会根据TYPE的值来决定如何读入和解释value的数据;

Key和value分别保存了键值对的键对象和值对象:

    1. key总是一个字符串对象;
    2. value根据底层保存的数据类型以及内容长度的不同,保存value的结构和长度也会有所不同;

带有过期时间的键值对:

 

      EXPIRETIME_MS常量的长度为1字节,作用为告知程序接下来将要读入的是一个以毫秒为单位的过期时间;

      ms是一个8字节长度的带符号整数,记录着一个以毫秒为单位的UNIX时间戳,该时间戳就是键值对的过期时间;

 

value的编码

      RDB文件中的每个value部分都保存了一个值对象,每个值对象的类型都由与之对应的TYPE记录,根据类型的不同,value部分的结构、长度也会不同;

  • 字符串对象

若TYPE的值为REDIS_RDB_TYPE_STRING,则value保存的对象类型为字符串类型,其编码可以分为REDIS_ENCODING_INTREDIS_ENCODING_RAW

若字符串对象的编码为REDIS_ENCODING_INT,则说明对象中保存的是长度不超过32位的整数:

 

      ENCODING的值可以是REDIS_RDB_ENC_INT8REDIS_RDB_ENC_INT16REDIS_RDB_ENC_INT32三个中的一个,分别代表RDB文件使用8位、16位、32位来保存整数值interger;

      若字符串对象的编码为REDIS_ENCODING_RAW,则说明对象保存的是字符串值,根据字符串长度不同,有压缩不压缩两种方式保存该字符串:

  • 若字符串长度小于等于20字节,则该字符串值会被原样保存;
  • 若字符串长度大于20字节,则该字符串会被压缩后再保存;

注:该条件是在服务器打开了RDB文件压缩功能的情况下进行,若关闭该功能,则字符串值总是以无压缩的方式保存字符串值;

无压缩存储方式:

len部分保存了字符串长度的,string部分保存了值本身;

压缩过后存储:

 

REDIS_RDB_ENC_LZF常量标志字符串已经经过LZF算法压缩、compressed_len保存着压缩过后字符串的长度、origin_len保存着原字符串长度、compressed_string保存着被压缩之后的字符串;

  • 列表对象

若TYPE的值为REDIS_RDB_TYPE_LIST,则value保存的是REDIS_ENCODING_LINKEDLIST编码的列表对象;

 

list_length记录了列表的长度,即列表保存了多少个项;

item部分为列表项,每个列表项都是一个字符串对象;

  • 集合对象

若TYPE的值为REDIS_RDB_TYPE_SET,则value保存的是REDIS_ENCODING_HT编码的集合对象,结构:

     

 

      set_size为集合的大小,记录了集合保存了多少个元素;

      elem代表集合的元素,每个元素都是一个字符串对象

  • 有序集合对象

若TYPE的值为REDIS_RDB_TYPE_ZSET则value保存的是一个有序集合对象,其底层编码为REDIS_ENCODING_SKIPLIST,结构如下;

 

sorted_set_size记录了有序集合的大小(即该有序集合保存了多少元素);

element为元素项,又分为成员分值两部分,前者为字符串对象,后者为double类型的浮点数(程序在保存double分值时会先将分值转换成字符串对象,再使用保存字符串对象的方式保存该分值),结构如下:

 

  • 哈希表对象

若TYPE的值为REDIS_RDB_TYPE_HASH,则value保存的是一个哈希表对象,其底层编码为:REDIS_ENCODING_HT

 

hash_size记录了哈希表的大小(即该哈希表中保存了多少个键值对);

key_value_pair则为具体的数据项(键值对),键和值都为字符串对象;

 

  • INTSET编码的集合

若TYPE的值为REDIS_TYPE_SET_INTSET,则value保存的是一个整数集合对象,RDB文件保存该对象的方法是,先将整数集合转换为字符串对象,然后将该字符串对象保存到RDB文件里面;

  • ZIPLIST编码的列表、哈希表、有序集合

若TYPE的值为REDIS_RDB_TYPE_LIST_ZIPLIST、REDIS_RDB_TYPE_HASH_ZIPLIST、REDIS_RDB_TYPE_ZSET_ZIPLIST,则value保存的是一个压缩列表对象,①将压缩列表转换成一个字符串对象、②将转换的字符串对象保存在RDB文件中;

分析RDB文件

      使用od命令来分析Redis服务器产生的RDB文件,该命令可以用给定的格式转存并打印输入文件;如,参数“-c”可以以ASCⅡ编码的方式打印输入文件,给定参数“-x”可以以十六进制的方式打印输入文件;

      具体使用:od  -c  xxx.rdb

重点总结:

  • RDB文件用于保存和还原Redis服务器数据库状态;
  • SAVE命令由服务器进程直接执行,该命令会阻塞服务器进程;
  • BGSAVE命令由子进程执行,所以不会阻塞服务器进程;
  • 服务器状态中会保存所有用save选项设置的保存条件,当任一保存条件被满足时,服务器会自动执行BGSAVE命令;
  • RDB文件是一个经过压缩的二进制文件,由多个部分组成;
  • 对于不同类型的键值对,RDB文件会使用不同的方式来保存;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值