前言
我们都知道redis的持久化策略分为RDB和AOF,但RDB和AOF底层是怎样去工作的呢?
RDB持久化
RDB持久化可以手动执行也可以自动执行,可以将redis的数据保存在RDB文件中,redis启动时加载RDB文件来恢复数据。
RDB的创建和载入
SAVE和BGSAVE命令都可以生成RDB文件,不同的是SAVE命令会阻塞redis进程,知道RDB创建完成前不能处理任何命令。BGSAVE则会派生一个子线程生成,服务器继续处理命令。
redis服务器在启动时,检测到RDB文件的存在会自动载入,当然因为AOF的更新频率更高,所以如果AOF文件和RDB文件都存在,会优先使用AOF恢复数据。
自动间隔性保存
除了可以输入命令手动生成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的文件结构
大写代表常量,小写代表变量和数据。
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是否出错或者损坏。
databases的结构
SELECTDB
:1字节长的字符常量,表示接下来要读取数据了。
db_number
:保存数据库号码,切换到不同的数据库,保证数据载入到正确的库中。
key_value_pairs
:保存着所有键值对数据,如果有过期时间也会保存在这里。
key_value_pairs的结构
如果没有过期时间,那么EXPIRETIME_MS和ms不存在。
EXPIRETIME_MS
:1字节长的字符常量,表示接下来读取的是一个过期时间。
EXPIRETIME_MS
:8字节长的带符号整数,记录着UNIX时间戳,也是键值对的过期时间。
AOF持久化
AOF是redis除了RDB外,另一种持久化方式。RDB保存的数据库的键值对,而AOF则通过保存执行的写命令来记录。AOF的实现可以分为命令追加、文件写入、文件同步三个步骤。
命令追加
服务器在执行每一次写命令后,会以协议的格式追加到aof_buf缓冲区里。
每次执行完时间事件或者文件事件后,会调用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重写缓冲区中,这样子进程执行完之后,再执行重写缓冲区的命令即可达到数据一致。