本章内容
Redis是一个内存数据库,所有数据都保存在内存中,一旦服务宕机,就会导致内存数据丢失。为了防止数据丢失,需要将内存数据持久化到磁盘。
Redis提供了两种数据持久化方式:RDB和AOF。
RDB
RDB持久化实现原理
RDB(Redis Database)持久化指的是将Redis中的内存数据集以快照(二进制)的方式写入磁盘文件,磁盘文件的后缀为.rdb(默认文件名:dump.rdb)。
如图所示:
RDB持久化主要步骤:
- 1)通过自动或手动的方式触发RDB持久化。
- 2)执行bgsave命令,检查是否存在其他子进程,如果存在其他子进程,则返回错误信息;否则主进程fork出一个子进程(子进程的所有数据与主进程一致,即:主、子进程共享内存数据)。
- 3)子进程读取内存数据,将数据写入一个RDB临时文件中。
- 4)子进程进行数据持久化时,主进程可以继续接收客户端请求,当客户端请求为写命令时,主进程通过COW(写时复制)机制拷贝共享内存中的数据到新内存,在新内存中对数据进行修改。
- 5)子进程RDB临时文件写入完成后通知主进程,主进程将RDB持久化期间变更的数据写入RDB临时文件中,写入完成后重命名RDB临时文件并替换旧的RDB文件,至此,RDB持久化完成。
bgsave命令:
class bgsave(){
// 创建子进程
pid=fork();
// 由子进程保存RDB文件
rdbSave();
// 保存成功后通知主进程
signal_parent();
}
RDB持久化触发方式
RDB持久化触发方式有两种:自动触发和手动触发。
自动触发
1)通过设置redis.conf配置文件中的save参数值来开启RDB自动持久化。
配置方式:
save 900 1:表示900秒内如果至少有1个key的值变化,则生成RDB文件
save 300 10:表示300秒内如果至少有10个key的值变化,则生成RDB文件
save 60 10000:表示60秒内如果至少有10000个key的值变化,则生成RDB文件
其中:
- save m n:表示m秒内数据集存在n次修改时,自动触发bgsave命令生成RDB文件。
2)通过执行redis-cli config set save ""命令或设置redis.conf配置文件中save参数为""来禁用RDB自动持久化。
配置方式:
save ""
#save 900 1
#save 300 10
#save 60 10000
其他相关配置:
- stop-writes-on-bgsave-error:RDB文件生成过程中发生错误时,是否停止Redis数据写入,默认为yes(停止)。
- rdbcompression :是否开启RDB文件压缩,默认为yes(开启)。
- rdbchecksum :是否开启RDB文件数据校验,默认为yes(开启)。
- dbfilename :RDB文件名,默认为dump.rdb。
- dir:RDB和AOF文件存储目录,默认为./(即:Redis安装路径)。
自动触发实现原理
Redis服务器程序会根据redis.conf文件中save参数设置的保存条件,设置服务器状态redisServer结构的saveparams属性。
redisServer结构:
struct redisServer{
// 记录保存save条件的数组
struct saveparam *saveparams;
// 修改计数器
long long dirty;
// 上一次执行保存的时间
time_t lastsave;
}
其中:
- dirty:记录上一次成功执行save命令或者bgsave命令后,Redis服务器数据修改次数(含:写入、删除、更新等)。
- lastsave:记录上一次成功执行save命令或者bgsave命令的时间,它是一个时间戳。
saveparams结构:
struct saveparam{
// 秒数
time_t seconds;
// 修改数
int changes;
};
如图所示:
Redis内部存在一个定时任务,默认100ms执行一次,可以通过参数hz设置,该参数值建议不要超过100(因为值越大执行频率越高,从而带来更多的CPU消耗,最终影响主进程读写性能。
该定时任务周期性的执行函数severCron(),该函数会遍历并检查saveparams数组中保存的所有条件,只要其中一个条件被满足,就会执行bgsave命令进行RDB持久化。bgsave命令执行完成后更新dirty值为0 、lastsave值为bgsave命令执行完成时间。
severCron函数:
def serverCron():
# ....
# 遍历所有保存条件
for saveparam in server.saveparams:
# 计算距离上次执行保存操作有多少秒
save_interval = unixtime_now() - server.lastsave
# 如果数据库状态的修改次数超过条件所设置的次数
# 如果距离上次保存的时间超过条件所设置的时间
if server.dirty >= saveparam.changes and save_interval > saveparam.seconds:
BGSAVE()
手动触发
RDB持久化手动触发命令:
- save:执行save命令时会阻塞当前Redis服务器,导致持久化期间不能处理客户端请求。
- bgsave:执行bgsave命令时会fork一个子进程进行异步持久化,持久化期间Redis服务器可以正常处理客户端请求。注意:fork操作会阻塞Redis服务器,时间很短。
另外,执行flushall命令时,也会产生dump.rdb文件,只是该文件内容为空。
RDB持久化优缺点
优点:
- 1)RDB内部存储的是Redis在某个时间点的数据快照,非常适合用于数据备份、全量复制等场景。
- 2)RDB恢复数据的速度比AOF快。
缺点:
- 1)RDB文件生成存在时间间隔,意外宕机时会造成数据丢失。
- 2)RDB文件采用二进制格式,存在版本兼容性问题。
- 3)fork创建子进程时,存在一定的内存消耗。
AOF
AOF(Append Only File)通过保存数据库执行命令来记录数据库的状态。
示例:
set str1 "123"
sadd str2 "1" "2" "3"
lpush str3 "1" "2" "3"
其中:
- RDB:将str1、str2、str3三个键值对保存到RDB文件中。
- AOF:将set、sadd、lpush三个命令保存到AOF文件中。
AOF持久化实现原理
AOF持久化主要分为命令追加、文件写入、文件同步三个步骤:
- 命令追加:当AOF持久化功能处于开启状态时, 服务器在执行完一个写命令后, 会以协议格式将写命令追加aof_buf(AOF文件数据缓冲区)中。
- 文件写入:Redis服务器进程是一个事件循环(loop), 该事件循环中:文件事件负责接收客户端命令请求并向客户端回复命令请求执行结果;时间事件负责执行定时函数(如:serverCron()等)。在每一个事件循环结束前,都会调用会调用flushAppendOnlyFile函数,将aof_buf中的内容保存到AOF文件中。
- 文件同步:AOF文件会根据appendfsync参数设置的持久化策略持久化(fsync)到磁盘。
文件写入流程,如图所示:
AOF持久化开启方式
通过设置redis.conf配置文件中的appendonly参数值来开启AOF持久化。
配置方式:
appendonly yes
appendfilename appendonly.aof
appendfsync everysec
其中:
- appendonly:默认为no,表示默认使用RDB持久化方式,如果需要开启AOF持久化,则需要将appendonly参数值修改为yes。
- appendfilename :AOF文件名,默认为appendonly.aof。
- appendfsync:AOF持久化策略(默认为everysec,推荐):
- always:每次写命令写入aof_buf(AOF文件数据缓冲区)后,同步将aof_buf中的写命令fsync到磁盘(注:fsync()函数用于将数据缓冲区的数据同步到磁盘)。
- everysec:每次写命令写入aof_buf后,每隔一秒将aof_buf中的写命令fsync到磁盘。
- no:每次写操作写入aof_buf后,aof_buf中写命令fsync到磁盘的时机交由操作系统控制。
其他相关配置:
- no-appendfsync-on-rewrite:AOF重写期间是否禁止appendfsync,默认为no(不禁止)。
- 如果将参数no-appendfsync-on-rewrite设置为no,AOF重写时,如果接收到客户端的写命令,则将写命令放入aof_buf中,并根据appendfsync配置的策略将aof_buf中的写命令fsync到磁盘。
- 如果将参数no-appendfsync-on-rewrite设置为yes,AOF重写时,如果接收到客户端的写命令,则将写命令放入aof_buf中,不会将写命令fsync到磁盘。
- auto-aof-rewrite-percentage:设置AOF重写基准值,默认为100,表示当前AOF文件大小超过上一次重写得到的AOF文件大小100%(即:当前AOF文件大小是上一次重写得到的AOF文件大小二倍时)时触发重写。
- auto-aof-rewrite-min-size:设置AOF重写基准值,默认为64mb,表示触发AOF重写的AOF文件最小值为64mb,用于避免AOF文件达到设定的百分比但AOF文件很小的情况下频繁触发AOF重写。
- aof-load-truncated:Redis启动时,加载的AOF文件发生末尾截断,是否继续加载并打印日志,默认为yes。
- yes:被加载的AOF文件发生末尾截断时,服务器会继续加载并打印日志信息通知用户。
- no:被加载的AOF文件发生末尾截断时,服务器会拒绝加载并启动失败,用户需要使用redis-check-aof工具修复AOF文件,再重新启动。
- dir:RDB和AOF文件存储目录,默认为./(即:Redis安装路径)。
AOF持久化优缺点
优点:
- 1)AOF持久化提供了多种的持久化策略,使用默认策略(everysec)时,在保证读写性能的情况下最多丢失1s的数据。
- 2)AOF文件采用追加的方式写入命令,由于某种原因(如:磁盘已满)以半写命令结束日志时,使用redis-check-aof工具很容易修正AOF文件。
- 3)AOF文件可读性较强,即使不小心使用FLUSHALL命令,如果在此期间未执行任何日志重写操作,可以手工修正错误文件(即:删除FLUSHALL命令),再使用AOF文件来恢复数据。
缺点:
- 1)对于相同的数据集,AOF文件通常比RDB文件更大。
- 2)不同的AOF持久化策略,AOF的处理效率可能低于RDB。
AOF重写
Redis运行过程中,随着命令的不断写入AOF文件将逐渐增大,AOF文件越大,启动加载时耗费的时间越长,因此,Redis引入了AOF重写机制。
AOF重写实现原理
如图所示:
其中:
- 浅蓝色部分为AOF持久化步骤。
- 浅红色部分为AOF重写步骤。
AOF重写主要步骤:
- 1)满足AOF重写条件,判断当前是否存在子进程,存在则退出AOF重写并返回错误信息,否则fork一个子进程。
- 2)子进程将历史内存数据写入AOF临时文件。
- 3)子进程进行AOF重写时,主进程可以继续接收客户端命令,并将接收的命令同时写入旧的AOF文件和aof_rewrite_buf(AOF重写缓冲区)中。
- 4)子进程完成AOF临时文件写入,通知主进程。
- 5)主进程收到子进程通知,将aof_rewrite_buf中的内容写入AOF临时文件,重命名AOF临时文件并替换掉旧的AOF文件。至此,AOF重写完成。
AOF重写触发方式
AOF重写有两种触发方式:
- 使用BGREWRITEAOF命令手动触发。
- 通过配置参数值自动触发,参数:
- auto-aof-rewrite-percentage:设置AOF重写基准值,默认为100,表示当前AOF文件大小超过上一次重写得到的AOF文件大小100%(即:当前AOF文件大小是上一次重写得到的AOF文件大小二倍时)时触发重写。
- auto-aof-rewrite-min-size:设置AOF重写基准值,默认为64mb,表示触发AOF重写的AOF文件最小值为64mb,用于避免AOF文件达到设定的百分比但AOF文件很小的情况下频繁触发AOF重写。
持久化方式选择
1)如果允许小段时间内数据丢失,可以使用RDB,RDB数据恢复比AOF效率更高更稳定。
2)如果不允许小段时间内数据丢失,可以使用AOF。
建议两种持久化方式配合使用,Redis启动时优先加载AOF文件来恢复原始数据,因为通常情况下AOF文件比RDB文件保存的数据集更完整。
混合持久化
Redis4.0版本后新增了RDB-AOF混合持久化方式,这种方式结合了RDB和AOF的优点,既能快速加载又能避免丢失过多的数据。
配置方式:
aof-use-rdb-preamble yes
其中:
- aof-use-rdb-preamble:默认为no,表示不开启混合持久化;设置为yes表示开启混合持久化。
开启混合持久化时,主进程会先fork出子进程将现有内存数据集以RDB方式写入AOF临时文件中,AOF临时文件写入完成后,通知主进程将aof_buf中的增量命令以AOF方式写入AOF临时文件,重命名含有RDB和AOF两种格式内容的AOF临时文件替换掉旧的AOF文件。即:混合持久化方式产生的文件同时包含RDB和AOF两种格式的内容。
数据恢复
将备份文件 (rdb文件或aof文件) 移至Redis安装目录并启动Redis服务,Redis会自动将备份文件数据加载至内存。加载备份文件期间,Redis服务器会一直处于阻塞状态,直到备份文件加载完成。
持久化文件加载规则:
- 如果只开启了AOF持久化,Redis启动时只会加载AOF文件进行数据恢复。
- 如果只开启了RDB持久化,Redis启动时只会加载RDB文件进行数据恢复。
- 如果同时开启了RDB和AOF持久化,Redis启动时只会加载AOF文件,进行数据恢复。
注意:在AOF开启的情况下,即使AOF文件不存在,只有RDB文件,也不会加载RDB文件。
数据恢复注意事项:
- 进行数据恢复时,需要先先停止Redis服务器,再执行数据恢复操作。否则正在运行的Redis服务器会覆盖恢复后的数据,导致数据丢失。
- 使用AOF文件恢复数据时,可能出现最后一部分写入数据丢失的情况,在进行数据恢复前,可以先检查最后一条写命令的执行情况,以确保数据的完整性。
- Redis数据文件过大时,恢复过程可能会比较耗时。可以考虑增量恢复方案(即:先加载部分数据,再逐步添加剩余数据,直到数据全部加载)。