redis:持久化RDB&AOF的底层实现

508 阅读5分钟

前言

我们都知道redis的持久化策略分为RDB和AOF,但RDB和AOF底层是怎样去工作的呢?

RDB持久化

RDB持久化可以手动执行也可以自动执行,可以将redis的数据保存在RDB文件中,redis启动时加载RDB文件来恢复数据。 image.png

RDB的创建和载入

SAVE和BGSAVE命令都可以生成RDB文件,不同的是SAVE命令会阻塞redis进程,知道RDB创建完成前不能处理任何命令。BGSAVE则会派生一个子线程生成,服务器继续处理命令。

redis服务器在启动时,检测到RDB文件的存在会自动载入,当然因为AOF的更新频率更高,所以如果AOF文件和RDB文件都存在,会优先使用AOF恢复数据。 image.png

自动间隔性保存

除了可以输入命令手动生成RDB文件,还可以通过配置redis.config自动生成RDB文件。

save 3600 1 300 100 60 10000

redis的时间事件100毫秒会执行一次serverCron函数,其中它会检查save选项保存的条件是否满足,如果满足就执行BGSAVE命令。

上述配置的含义是:

  • 服务器在3600秒内,执行了至少1次修改。
  • 服务器在300秒内,执行了至少100次修改。
  • 服务器在60秒内,执行了至少10000次修改。

dirty计数器和lastsave属性

redisServer中还保存着dirty、lastsave。

struct redisServer {
    // ....
    // 修改计数器
    long long dirty; 
    // 上一次保存时间
    time_t lastsave;
    // ....
}

dirty:表示上次保存之后,进行了多少次修改。
lastsave:表示上次保存的时间。
serverCron函数就是通过dirty和lastsave判断,如果满足了save条件中的一种,就执行保存。

RDB的文件结构

image.png 大写代表常量,小写代表变量和数据。
REDIS:RDB文件的开头,是一个REDIS的字符常量,用于检测是否是RDB文件。
db_version:长度4字节,表示RDB文件的版本。
databases:保存着所以非空库的数据,如果所有库都没有数据,那么这个部分也为空0字节。
EOF:也是长度为1字节的字符常量,代表RDB文件结束。
check_sum:8字节长的无符号整数,保存一个校验和。通过REDIS、db_version、databases、EOF计算得出, 载入RDB文件会将载入的数据计算出的校验和与check_sum比较,来检查RDB是否出错或者损坏。 image.png

databases的结构

image.png
SELECTDB:1字节长的字符常量,表示接下来要读取数据了。
db_number:保存数据库号码,切换到不同的数据库,保证数据载入到正确的库中。
key_value_pairs:保存着所有键值对数据,如果有过期时间也会保存在这里。

key_value_pairs的结构

image.png
如果没有过期时间,那么EXPIRETIME_MS和ms不存在。
EXPIRETIME_MS:1字节长的字符常量,表示接下来读取的是一个过期时间。
EXPIRETIME_MS:8字节长的带符号整数,记录着UNIX时间戳,也是键值对的过期时间。

AOF持久化

AOF是redis除了RDB外,另一种持久化方式。RDB保存的数据库的键值对,而AOF则通过保存执行的写命令来记录。AOF的实现可以分为命令追加、文件写入、文件同步三个步骤。 image.png

命令追加

image.png
服务器在执行每一次写命令后,会以协议的格式追加到aof_buf缓冲区里。

image.png 每次执行完时间事件或者文件事件后,会调用flushAppendOnlyFile函数,将缓冲区的数据写入到AOF文件中。flushAppendOnlyFile的行为,由服务器配置的appendfsync决定。
always:每次都写入到AOF文件,最安全,最多丢失一次的数据,但是也是效率最低的。
everysec:每1秒将缓冲区数据同步到AOF文件。因此可能丢失掉1秒内的数据。(默认值)
no:何时将缓冲区数据同步到AOF文件,由操作系统决定。

AOF重写

> set k1 v1
OK
> set k1 v2
OK
> set k1 v3
OK

上图,3条命令将k1从v1,设值成v2,然后从v2设值成v3。可以想象每次写命令都会写入到AOF文件中,导致AOF文件越来越臃肿。

但实际上上面这种情况AOF文件中直接保存一条命令set k1 v3即可。这样可以对AOF文件实现“瘦身”。

这样的“瘦身”又被称为AOF重写。

AOF重写,重写这个是比较容易造成歧义的。事实上它不会对现有的AOF文件进行分析和重写。它是通过读取当前的数据库状态实现的。

AOF后台重写

redis会创建一个子进程来处理AOF的重写,这样redis的服务也不会阻塞住。子进程也带有一个数据副本,不用使用锁,也可以保证数据的安全性。

但是这样会带来一个问题,子线程在处理AOF重写的同时,redis还在执行新的写入命令。这样就会导致AOF文件和数据库不一致。

AOF重写缓冲区

为了解决这个这个不一致问题,服务器创建子进程后,除了将写命令追加到AOF缓冲区外,还会追加到AOF重写缓冲区中,这样子进程执行完之后,再执行重写缓冲区的命令即可达到数据一致。

image.png