redis的RDB和AOF

2020-08-11  本文已影响0人  一拳超疼

RDB持久化

RDB文件是经过压缩的二进制文件,可以通过使用了save和bgsave命令主动地生成RDB文件,两者有以下不同:

与生成RDB文件不同的是,载入RDB文件是无法通过客户端来控制的,只用当服务器启动的时候,会自动检测RDB文件并且加载。
值的一提的是:因为AOF文件的更新频率通常比RDB文件的更新频率高,所有当服务器开启了AOF持久化功能时,服务器启动会优先使用AOF文件来还原数据库状态。如果未开启,则是默认RDB文件还原数据库状态。
服务器在载入RDB文件时,整个期间会处于阻塞的状态,无法接受客户端命令,直到工作完成。

RDB的自动间隔保存

Redis支持配置服务器自动保存RDB文件,在redis.conf配置如下:

save 900 1
save 300 10
save 60 10000

以上配置的意思是:

在满足上面的一个条件,Redis服务器会执行bgsave命令,fork主进程得到子进程,生成RDB文件。
服务器是如何判断达到生成RDB文件的条件?
答:在redis中维持着一个redisServer结构体,其中一些属性起到作用:

struct redisServer {
    // 记录了保存条件的数组
    struct saveparam *saveparams;
    // 修改计数器
    long long dirty;
    // 上一次执行保存的时间
    time_t lastsave;
}

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

saveparams是一个数组,其中存储着redis.conf中配置的三个秒数时间属性值(seconds)900、300、60,对应着时间段内的修改值changes:

saveparams[0] saveparams[1] saveparams[2]
seconds 900 seconds 300 seconds 60
changes 1 changes 10 changes 10000

此外在RedisServer结构体中,还存在dirty(计数器)和lastsave。

当服务器成功执行了一个数据库修改命令后,dirty计数器就会加一。Redis服务器中serverCron()方法默认每100毫秒执行一次检查,如果满足条件就执行bgsave。

RDB文件结构

REDIS db_version databases EOF check_sum

REDIS:是文件最开头的"REDIS"五个字符,通过这五个字符可以判断载入的文件是否RDB文件。
db_version:长度为4个字节,它的值是一个字符串表示的整数,记录着RDB文件的版本号。
databases:包含所有数据库,以及各个数据库中的键值对数据。
EOF:长度为1字节,该常量标志着RDB文件正文内容结束。
check_sum:RDB文件的校验和,由前四个值计算出来。

AOF持久化

与RDB文件通过保存数据库中键值对来记录数据库状态不同,AOF持久化是通过记录数据库的写命令来记录数据库状态的。

AOF持久化的实现

AOF持久化功能的是实现可以分为命令追加(append)、文件写入、文件同步(sync)三个步骤。

命令追加

当AOF处于开启状态时,服务器在执行完一个写命令之后,会以协议的格式追加到服务器状态的aof_buf缓存区的末尾:

struct redisServer {
    // AOF缓冲区
    sds aof_buf;
}

这里体现了redis中使用sds(简单动态字符串)作为缓冲区的用法。

AOF文件的写入和同步

首先,redis的服务器进程是一个事件循环,每次在处理完文件事件后可能会执行写命令,使得一些内容被追加到aof_buf缓冲区中,所以服务器每次在结束一个事件循环后,都会调用flushAppendOnlyFile函数,考虑是否需要将aof_buf缓冲区中的内容写入和保存到AOF文件里面。
flushAppendOnlyFile函数会根据配置的appendsync选项的值来进行写入和同步AOF文件的时机。

appendsync选项的值 flushAppendOnlyFile函数的行为
always 将aof_buf缓冲区中的所有内容写入到内存缓冲区并同步到AOF文件。
everysec 将aof_buf缓冲区中的所有内容写入到内存缓冲区中,如果距离上一次同步AOF文件的时间超过一秒,那么进行同步,且同步工作给子线程做。
no 将aof_buf缓冲区中的所有内容写入到内存缓冲区中,但不进行同步,何时同步到AOF文件,由操作系统决定。

AOF文件的载入与数据还原

因为,AOF文件中包含了重建数据库所需的所有写命令,所以服务器只要读入并重新执行一遍AOF文件中的所有写命令,就可以还原redis服务器关闭之前的所有状态。
Redis读取AOF文件并还原数据库状态的详细步骤如下:

  1. 创建一个不带网络的伪客户端(fake client):因为redis的命令只能在客户端执行。
  2. 从AOF文件中分析并读取出一条写命令。
  3. 使用伪客户端执行被读出的写命令。
  4. 重复执行2,3,直到命令被处理完毕。

AOF重写

AOF重写的根本目的就是解决AOF文件过大的问题,随着服务器运行,AOF文件追加的内容越来越多,导致AOF文件体积越来越大,会导致使用AOF文件恢复数据的时间过长。
AOF重写会重新生成新的AOF文件替代旧的文件,但是重写的AOF文件中不会包含任何浪费空间的冗余命令,所以新生成的文件体积通常会比旧文件的体积小得多。

AOF文件重写的实现

AOF重写时并不会对旧AOF文件进行任何解析,而是有点像RDB持久化,读取数据库的键对应的值,不过重写不是和RDB一样保存键值对,而是以写入数据的命令的形式来进行记录数据。例如:

# 存在键值对 name : zhang
set name zhang # 以写入数据的形式来记录数据

因为aof_rewrite()函数生成的新文件只包含还原当前数据库状态所必须的命令,所以新AOF文件不会浪费任何硬盘空间。

AOF后台重写

上面介绍重写使用的aof_rewrite()函数可以很好的完成创建新的AOF文件的任务,但是该函数会阻塞redis主进程。所以这里介绍后台重写。
Redis将AOF重写程序放到子进程中执行,这样可以达到两个目的:

使用子进程的方式虽然可以避免服务器进程的阻塞,但是也会带来数据不一致的问题。
为了解决数据不一致的问题,redis设置了一个AOF重写缓冲区,当子进程在进行AOF重写工作时,服务器还会继续执行写入命令,此时,服务器会向AOF缓冲区和AOF重写缓冲区写入写命令。
当AOF重写任务完成时,子进程会向父进程发送一个完成信号,父进程接收到信号后,将AOF重写缓冲区中的所有内容写到新AOF文件中,然后完全的AOF重写文件会改名,并原子地覆盖现有的AOF文件。

上一篇 下一篇

猜你喜欢

热点阅读