引言
Redis是一个内存数据库,它将自己的数据库状态存在内存里面。如果不想办法将存储在内存的数据库状态保存到磁盘里面,一旦服务器进程退出,那么数据也就不见了。
为了解决这个问题,Redis提供了RDB和AOF持久化功能,将Redis内存中的数据保存到磁盘里面,避免意外丢失。
一、RDB持久化
RDB持久化功能所生成的RDB文件是一个经过压缩的二进制文件,通过该文件还可以还原生成RDB文件时的数据库状态。
1、RDB文件的创建与载入
1.1、RDB的创建
有两个命令可以用于生成RDB文件:SAVE和BGSAVE。
SAVE命令会阻塞Redis服务器进程,直到RDB文件创建完毕,在服务器进程阻塞期间,服务器不能处理任何命令请求。
BGSAVE命令会派生出一个子进程,然后由子进程负责创建RDB文件,服务器进程(父进程)继续处理命令请求。
1.2、RDB的载入
Redis没有专门用于载入RDB文件的命令,只要服务器启动时检测到RDB的存在,就会自动载入RDB文件。在RDB文件载入期间,服务器会一直处于阻塞状态。 成功载入后日志如下:
1.3、RDB载入的条件
AOF文件的更新频率通常比RDB高,所以对于服务器启动过程载入RDB的条件为:
- 如果服务器开启了AOF持久化功能,服务器会优先使用AOF文件来还原数据库状态
- 只有在AOF持久化功能处于关闭状态时,服务器才会使用RDB文件来还原数据库状态
2、RDB的自动间隔性保存
通过save选项设置多个保存条件,可以让服务器每隔一段时间自动执行BGSAVE命令。比如,设置如下配置:
save 900 1 // 900秒内,对数据库进行了至少1次修改
save 300 10 // 300秒内,对数据库进行了至少10次修改
save 60 10000 // 60秒内,对服务器进行了至少10000次修改
只要满足3个条件中任意一个,BGSAVE命令就会执行。
默认情况下,Redis的周期操作函数serverCron每隔100毫秒会执行一次,检查是否满足条件进行持久化操作。 如果满足的话,就会执行BGSAVE命令。
save选项所设置的保存条件,保存在服务器状态redisServer结构的saveparams属性中;此外dirty属性和lastsave属性用于辅助持久化操作:
struct redisServer {
// ...
// 记录了保存条件的数组
struct saveparam *saveparams;
// ...
// 修改计数器
long long dirty;
// 上一次执行保存的时间
time_t lastsave;
}
struct saveparam {
// 秒数
time_t seconds;
// 修改数
int changes;
}
二、AOF持久化
AOF持久化是通过保存Redis服务器所执行的写命令来记录数据库状态。
1、AOF的实现
AOF持久化功能的实现可以分为:命令追加(append)、文件写入、文件同步(sync)。
struct redisServer{
// ...
// AOF缓冲区
sds aof_buf;
// ...
}
命令追加:当AOF持久化功能打开时,服务器在执行完一个写命令后,会以协议格式将被执行的写命令追加到服务器状态的aof_buf缓冲区的末尾
文件写入与同步:Redis服务器进程是一个事件循环,每次事件循环结束之前,会调用flushAppendOnlyFile函数,考虑是否将aof_buf缓冲区的内容写入到AOF文件中。flushAppendOnlyFile函数行为由服务器配置appendfsync选项的值来决定。
2、AOF文件的载入与数据还原
AOF文件包含了重建数据库状态所需的所有写命令,所以服务器只需要重新执行一遍AOF文件中的所有命令,就可以还原到服务器关闭之前的数据库状态。
读取AOF文件并还原步骤如下:
- 创建一个不带网络连接的伪客户端
- 从AOF文件中分析并读取出一条写命令
- 使用伪客户端执行被读出的写命令
- 重复步骤2和3,知道AOF所有命令都处理完
3、AOF重写
随着写入命令的增加,AOF文件内容越来越多,文件体积越来越大。体积过大的AOF会影响到Redis服务器甚至宿主机器,而且使用AOF还原所需的时间也越多。针对这个问题,Redis提供了文件重写功能:创建一个新的AOF来替代现有AOF文件,新AOF不会有任何浪费空间的冗余命令。
3.1、AOF重写原理
AOF重写并不需要依赖现有的AOF文件,而是通过读取服务器当前的数据库状态来实现:首先从数据库中读取键现在的值,然后用一条命令记录该键值对,代替之前这个键值对的多条命令。
AOF重写使用BGREWRITEAOF命令,该命令会创建一个子进程来完成AOF重写。Redis服务器设置了一个AOF重写缓冲区,这个缓冲区在创建子进程之后开始使用。重写的过程如下:
- 当Redis执行完一个写命令后,同时往AOF缓冲区和AOF重写缓冲区发送。
- 当子进程完成重写后,向父进程发送信号
- 父进程接收到完成信号后,将AOF重写缓冲区的写命令追加到新的AOF文件中
- 父进程用新AOF文件覆盖旧AOF。整个重写过程就算完成了。
3.2、AOF自动重写触发条件
AOF功能开启的情况下,会维持三个变量:
- aof_current_size:当前AOF文件大小
- aof_rewrite_base_size:最后一次AOF重写之后,AOF文件大小
- aof_rewrite_perc:增长百分比
同时redis配置中,有两个属性:
- auto-aof-rewrite-min-size:运行AOF重写时文件最小体积, 默认为64MB
- auto-aof-rewrite-percentage:当前AOF文件空间( aof_current_size) 和上一次重写后AOF文件空间(aof_rewrite_base_size) 的比,默认100
自动触发AOF重写条件:
- 当前没有RDB或AOF持久化操作在执行
- 没有BGREWRITEAOF在进行
- 当前AOF大小大于配置的最小AOF重写体积(auto-aof-rewrite-min-size),并且AOF增长百分比大于等于配置增长百分比(auto-aof-rewrite-percentage)
总结
1、Redis没有专门命令载入RDB文件,服务器启动初始化时,会自动载入RDB;载入RDB前提是服务器没有开启AOF持久化功能,否则会优先载入AOF
2、SAVE保存RDB会使服务器阻塞,BGSAVE由子进程执行保存操作所以不会阻塞
3、Redis默认每隔100毫秒检查是否满足条件保存RDB,满足则进行保存
4、服务器每执行完一个写命令,会往AOF缓冲区发送该写命令;通过Redis的事件循环调用flushAppendOnlyFile函数将缓冲区的写命令同步到AOF文件中,同步时机服务器由配置appendfsync选项的值来决定
5、AOF重写是通过数据库中的键值对来完成;重写过程中会启用AOF重写缓冲区来保存服务器接收并处理的写命令