Redis 持久化——RDB 详解

目录

1.1 RDB 概述

1.2 RDB 持久化执行方式

1.3 RDB 文件的创建与载入

1.4 自动化执行原理


Redis 是一个键值对数据库服务器,服务器中通常包含着任意个非空数据库,而每个非空数据库中有可以包含任意个键值对,为了方便起见,我们将服务器中的非空数据库以及它们的键值对统称数据库状态

Redis 是内存数据库,它将自己的数据库状态储存在内存里,如果不想办法将储存在内存中的数据库状态保存到磁盘里,那么一旦服务器进程退出,服务器中的数据库状态也会消失不见。为了解这个问题,Redis 提供了两种持久化方式—— RDB(Redis DataBase) 和 AOF(Append Only File) ,这可以将 Redis 在内存中的数据库状态保存到磁盘里。

下面详细介绍一下 RDB 持久化,希望能帮助到其他小伙伴~ 关于 AOF 持久化详解 可参考下一篇。

1.1 RDB 概述

RDB 其实就是把数据库状态以快照的形式保存在磁盘上。什么是快照呢,你可以理解成把当前时刻的数据拍成一张照片保存下来。

RDB 持久化是指可以将某个时间点上的数据库状态保存到一个 RDB 文件中。RDB 文件是经过压缩的二进制文件,默认的文件名为dump.rdb。因为 RDB 文件是保存在磁盘,所以不会丢失。

1.2 RDB 持久化执行方式

RDB 持久化可以手动执行(SAVE、BGSAVE),也可以根据服务器配置选项定期执行(自动化)。

1. 执行SAVE 命令时,会阻塞当前 Redis 服务器,执行 SAVE 命令期间,Redis 不能处理其他命令,直到 RDB 文件创建完毕为止。

redis> SAVE    //等待 直到RDB文件创建完毕
OK

2. 执行 BGSAVE 命令时,Redis 会在后台异步进行快照操作,快照同时还可以响应客户端请求。具体操作是 Redis 进程执行 fork 操作创建子进程,RDB 持久化过程由子进程负责,完成后自动结束。阻塞只发生在 fork 阶段,一般时间很短。

redis> BGSAVE    //派生子进程,并由子进程创建RDB文件
Background saving started

基本上 Redis 内部所有的 RDB 操作都是采用 BGSAVE 命令

3. 根据 redis.conf 配置里的 save m n 定时执行用的是BGSAVE),只要其中任意一个条件被满足,服务器就会执行 BGSAVE 命令。

服务器的 save 选项设置默认条件:

  • save 900  1         服务器在900秒内,对数据库进行了至少1次修改
  • save 300  10       服务器在300秒内,对数据库进行了至少10次修改
  • save 60    10000 服务器在60秒内,对数据库进行了至少10000次修改

只要满足以上任意条件服务器就会执行 BGSAVE 命令。如果想禁用快照保存的功能, 可以通过注释掉所有 "save" 配置达到,或者在最后一条 配置后添加:save ""。

因为 BGSAVE 命令的保存工作是由子进程执行的,所以在子进程创建 RDB 文件的过程中,Redis服务器仍然可以继续处理客户端的命令请求,但是,在 BGSAVE 命令执行期间,服务器处理 SAVE、BGSAVE、BGREWRITEAOF三个命令的方式和平时有所不同:

首先,在 BGSAVE 命令执行期间,客户端发送的 SAVE 命令会被服务器拒绝,服务器禁止 SAVE 命令和 BGSAVE 命令同时执行是为了避免父进程(服务器进程)和子进程同时执行两个 rdbSave 调用,防止产生竞争条件;

其次,在 BGSAVE 命令执行期间,客户端发送的 BGSAVE 命令会被服务器拒绝,因为同时执行两个 BGSAVE 命令也会产生竞争条件;

最后,BGREWRITEAOF 和 BGSAVE 两个命令不能同时执行:①如果 BGSAVE 命令正在执行,那么客户端发送的 BGREWRITEAOF 命令会被延迟到 BGSAVE 命令之执行完毕之后执行;②如果 BGREWRITEAOF 命令正在执行,那么客户端发送的 BGSAVE 命令会被服务器拒绝。

因为 BGREWRITEAOF 和 BGSAVE 两个命令的实际工作都是由子进程执行,所以两个命令在操作方面并没有什么冲突的地方,不能同时执行它们只是一个性能方面的考虑——并发出两个子进程,并且这两个子进程都同时执行大量的磁盘写入操作,这怎么想都不会是一个好主意。

1.3 RDB 文件的创建与载入

创建 RDB 文件的实际工作由 rdb.c/rdbSave 函数完成,SAVE 命令和 BGSAVE 命令会以不同的方式调用这个函数,通过一下伪代码可以明显看出这两个命令的区别:

def SAVE () :
    # 创建RDB文件
    rdbSave()


def BGSAVE () :
    # 创建子进程
    pid = fork()
    if pid == 0 :
        # 子进程负责创建RDB文件
        rdbSave()
        #完成之后向父进程发送信号
        signal_parent()
    elif pid > 0 :
        # 父进程继续处理命令请求,并通过轮询等待子进程的信号
        handle_request_and_wait_signal()
    esle :
        # 处理出错情况
        handle_fork_error()

和使用 SAVE 命令或者 BGSAVE 命令创建 RDB 文件不同,RDB 文件载入工作是在服务器启动时自动执行,所以 Redis 并没有专门用于载入 RDB文件的命令,只要 Redis 服务器在启动时检测到 RDB 文件存在,就会自动载入它。服务器在载入 RDB 文件期间,会一直处于阻塞状态,直到载入工作完成为止。

以下是 Redis 服务器启动时打印的日志记录,其中第二条日志 DB loading from disk:…就是服务器在成功载入 RDB 文件之后打印的:

$ redis-server
[7379] 06 Aug 21:07:01.270 # Server started, Redis version 2.9.11
[7379] 06 Aug 21:07:01.289 * DB loading from disk:0.018 seconds
[7379] 06 Aug 21:07:01.289 * The server is now ready to accept connections on port 6379

载入 RDB 文件的实际工作有 rdb.c/rdbLoad 函数完成。这个函数和 rdbSave 函数之间的关系可以用图 1-3-1 表示。

1.4 自动化执行原理

当Redis 服务器启动时,用户可以通过指定配置文件或者传入启动参数的方式设置 save 选项,接着,服务器程序会根据 save 选项所设置的保存条件,设置服务器状态 redisServer 结构的 saveparams 属性:

struct redisServer {
    // ...
    
    //记录了保存条件的数组
    struct saveparam * saveparams;

    // ...
};

saveparams 属性是一个数组,数组中的每个元素都是一个 saveparam 结构,每个 saveparam 结构都保存了一个 save 选项设置的保存条件:

struct saveparam {
    // 秒数
    time_t seconds;
    // 修改数
    int changes;
};

比如说,save选项的值为默认条件,那服务器状态中的 saveparams 数组如图 1-4-1 所示。

除了 saveparams 数组之外,服务器状态还维持着一个 dirty 计时器,以及一个 lastsave 属性

dirty 计数器记录距离上一次成功执行 SAVE 命令或者 BGSAVE 命令之后,服务器对数据库状态(服务器中的所有数据库)进行了多少次修改(包括写入、删除、更新等操作);

lastsave 属性是一个 UNIX 时间戳,记录服务器上一次成功执行 SAVE 命令或者 BGSAVE 命令的时间。

struct redisServer {
    // ...

    // 修改计数器
    long long dirty
    // 上一次执行保存时间
    time_t lastsave

    // ...

};

当服务器成功执行一个数据库修改命令之后,程序就会对 dirty 计数器进行更新:命令修改了多少次数据库,dirty 计数器的值就增加多少。

例如,如果我们为一个字符串键设置值:

redis> SET message "hello"
OK

那么程序就会将 dirty 计数器加1。

如果我们想一个集合键增加三个新元素:

redis> SADD database Redis MongoDB MariaDB
(integer) 3

那么程序会将 dirty 计数器的值增加3。

如图 1-4-2 展示了服务器中包含的dirty 计数器 和 lastsave 属性。

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

参考:

《Redis设计与实现》  黄建宏 著

https://blog.csdn.net/xinbumi/article/details/89742930

https://baijiahao.baidu.com/s?id=1654694618189745916&wfr=spider&for=pc

 

  • 4
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Redis持久化是为了避免进程退出导致数据的永久丢失而设计的。由于Redis是基于内存的数据库,数据存储在内存中,关闭服务或者断电会导致数据丢失。为了解决这个问题,Redis提供了两种持久化方式:AOF(Append Only File)和RDBRedis Database File)。 AOF持久化是通过将写操作追加到AOF文件中来实现的。AOF文件是一个只追加的日志文件,记录了写操作的命令。当Redis重启时,Redis会根据AOF文件中的命令重新执行一遍,从而恢复数据。AOF文件的大小会随着写操作的增加而增大,因此可能会占用较大的磁盘空间。为了避免AOF文件过大,Redis提供了AOF重写机制,可以定期地将AOF文件重写为紧凑格式,只保留可以恢复数据库状态的最小命令集合。 RDB持久化是通过将当前数据库状态快照保存到一个二进制文件中来实现的。RDB文件是一个经过压缩的二进制文件,包含了数据库的数据和键值对的过期时间等信息。RDB持久化是通过fork子进程来实现的,它会将当前数据库状态保存到一个临时文件中,然后替换原来的RDB文件。RDB持久化适用于数据备份和灾难恢复。 除了持久化之外,Redis还支持快照机制。快照是将当前数据库状态保存到一个RDB文件中,可以手动触发或者通过配置选项定期触发。快照只保存了数据库的最新状态,而不是增量的写操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值