Redis是内存数据库,宕机后数据会消失。
Redis重启后快速恢复数据,需要提供持久化机制
RDB
RDB(Redis DataBase)是Redis默认的存储方式,RDB是通过快照(snapshotting)完成的
触发方式
- 符合自定义配置的快照规则
- 执行 save 或者 bgsave 命令
- 执行 flush 命令
- 执行主从复制操作(第一次)
配置参数
# redis.conf
save <seconds> <change>
save "" # 不使用RDB存储
save 900 1 # 表示15分钟内至少1个key被更改则进行快照
save 300 10 # 表示5分钟内至少10个键值被更改则进行快照
save 60 1000 # 表示1分钟内至少1000个键值被更改则进行快照
TIPS:漏斗设计,提高性能
原理
- Redis父进程首先判断当前是否在执行save命令,或者bgsave/bgrewriteaof的子进程,如果在执行直接返回
- 父进程执行fork(调用OS函数复制主进程)操作创建子进程,这个复制过程中父进程是阻塞的,Redis不能执行来自客户端的任何命令
- 父进程fork后,bgsave命令返回“Background saving started”信息并不再阻塞父进程,并可以响应其他命令
- 子进程创建RDB文件,根据父进程内存快照生产临时快照文件,完成后对原有文件进行原子替换
- 子进程发送信号给父进程表示完成,父进程更新统计信息
- 父进程fork子进程后,继续工作

文件结构
"REDIS" | RDB_VERSION | AUX_FIELD_KEY_VALUE_PAIRS | DB_NUM | DB_DICT_SIZE | EXPIRE_DICT_SIZE | KEY_VALUE_PAIRS | EOF | CHECK_SUM |
---|
1、头部5字节固定位“REDIS”
2、4字节“RDB”版本号,当前为9,填充后0009
3、辅助字段,以key-value的形式
字段名 | 字段值 | 字段名 | 字段值 |
---|---|---|---|
redis-ver | 5.0.5 | aof-preamble | 是否开启AOF |
redis-bits | 64/32 | repl-stream-db | 主从复制 |
ctime | 当前时间戳 | repl-id | 主从复制 |
used-mem | 使用内存 | repl-offset | 主从复制 |
4、存储数据库号码
5、字典大小
6、过期key
7、主要数据,以key-value形式存储
8、结束标识
9、校验和,就是看文件是否损坏或者是否被修改
优缺点
-
优点
RDB是二进制压缩文件,占用空间小,便于传输(主从同步)
主进程fork子进程,可以最大化redis性能,主进程不能太大,复制过程中主进程阻塞 -
缺点
不保证数据完整性,会丢失最后一次快照以后更改的所有数据
AOF
AOF(append only file)是Redis的另一种持久化方式。Redis默认情况下是不开启的。
开启AOF之后,Redis将所有对数据库进行过写入的命令(及其参数)(RESP)记录到AOF,以此达到记录数据库状态的目的。
当Redis重启后只要按照顺序回放这些命令就会恢复到原始状态
AOF会记录过程,RDB只管结果
配置
appendonly yes # 开启
# AOF文件的保存位置,和RDB文件的位置相同
dir ./
# 默认的文件名是appendonly.aof
appendfilename appendonly.aof
原理
AOP文件中存储的是Redis的命令,同步命令到AOF文件的整个过程可以分为三个阶段:
-
命令传播
服务器在收到请求后,它会根据协议文本的内容选择适当的命令函数,并将各个参数从字符串文本转化为Redis字符串对象(StringObject)。每当命令函数成功执行后,命令、命令的参数、命令的参数个数等信息发送到AOF程序中 -
缓存追加
AOF程序根据接收到的命令数据,将命令从字符串对象转换回原来的协议格式,然后将协议内容追加到服务器的AOF缓冲中(redisServer结构的aof_buf),aof_buf域则保存着所有等待写入到AOF文件的协议文本(RESP) -
文件写入或保存
每当服务器常规任务函数被执行、或者事件处理器被执行时,flushAppendOnlyFile
函数都会被调用,这个函数执行以下两个工作- WRITE:根据条件将aof_buf中的缓存写入到AOF文件(内存中)
- SAVE:根据条件调用
fsync
或fdatasync
函数,将AOF文件保存到磁盘中
保存模式
// redis.conf
# appendfsync always
appendfsync everysec
# appendfsync no
-
不保存(no)
每次调用flushAppendOnlyFile
函数,WRITE 都会被执行,SAVE 会被略过
以下三种情况 SAVE 才会被执行(会引起 Redis 主进程阻塞)- Redis被关闭
- AOF功能被关闭
- 系统写缓存被刷新(可能是缓存已经被写满或者定期保存操作被执行)
-
每秒保存一次(everysec)
原则上每隔一秒就会执行一次,因为 SAVE 操作是由后台子线程(fork)调用的,所以不会引起服务器主进程阻塞 -
每执行一个命令保存一次(always)
每次执行完一个命令后,WRITE 和 SAVE 都会被执行。因为 SAVE 是由 Redis 主进程执行的,所以 SAVE 执行期间,主进程会阻塞不能接受命令请求
TIPS:WRITE 永远阻塞主进程,SAVE 只有在 everysec 模式下才不阻塞主进程
AOF重写
AOF记录数据的变化过程,执行命令越来越多,AOF记录越来越大,此时需要重写瘦身
Redis会在AOF体积变得过大时自动在后台(fork子进程)对AOF进行重写。重写后新的AOF文件包含了恢复当前数据集所需的最小命令集合。
set s1 11
set s1 22
set s1 33
优化后:
set s1 33lpush list1 1 2 3
lpush list1 4 5 6
优化后:
lpush list1 1 2 3 4 5 6
Redis不希望AOF重写造成服务器无法处理请求,所以AOF重写程序在后台子进程中执行,这样的好处:
- 子进程进行AOF重写期间,主进程可以继续处理命令请求
- 子进程带有主进程的数据副本,使用子进程而不是线程,可以在避免锁的情况下,保证数据的安全性
同时子进程也引发新的问题:
子进程在进行AOF重写期间,主进程还需要继续处理命令,而新的命令可能对现有的数据进行修改,会导致当前数据和重写后的AOF文件中的数据不一致
解决方案:
Redis增加了一个AOF重写缓存,这个缓存在fork子进程之后开始启用,Redis主进程在接到新的命令之后,除了将这个写命令的协议内容追加到现有的AOF文件之外,还会追加到这个缓存中。当子进程完成AOF重写之后,它会向父进程发送一个完成信号,父进程在收到完成信号后,调用一个信号处理函数,并完成以下工作:
1、将AOF重写缓存中的内容全部写入到AOF文件中
2、对新的AOF文件进行改名,覆盖原有AOF文件
![]()
触发方式
# redis.conf
# 限制允许重写最小aof文件大小,也就是文件大小小于64mb的时候,不需要进行优化
auto-aof-rewrite-min-size 64mb
# 表示当前aof文件大小超过上一次aof文件大小的百分之多少的时候会进行重写,如果之前没有重写过,以启动时aof文件大小为准
auto-aof-rewrite-percentage 100
或者
127.0.0.1:6379> bgrewriteaof
混合持久化
Redis4.0开始支持 rdb 和 aof 的混合持久化。如果把混合持久化打开,aof rewrite 的时候就直接把 rdb 的内容写到 aof 文件开头
RDB的头 + AOF的身体 ------> appendonly.aof
# redis.conf
# 开启混合持久化
aof-use-rdb-preamble yes
AOF文件载入
AOF文件里面包含了重建数据库状态所需要的所有写入命令,所以服务器只要读入并重新执行一遍AOF文件里面保存的写命令,就可以还原服务器关闭之前的数据库状态
- 创建一个不带网络连接的伪客户端(fake client)
- 从AOF文件中分析并读取一条写命令
- 使用伪客户端执行被读出的写命令
- 一直执行步骤2和步骤3,直到AOF文件中的所有写命令都被处理完毕为止
对比
- RDB存某个时刻的数据快照,采用二进制压缩存储;AOF存操作命令,采用文本存储(混合)
- RDB性能高;AOF性能较低
- RDB在配置触发状态会丢失最后一次快照以后更改的所有数据;AOF设置为每秒保存一次,则最多丢2秒的数据
- Redis以主程序模式运行,RDB不会保存过期键值对数据,Redis以从服务器模式运行,RDB会保存过期键值对,当主服务器向从服务器同步时,再清空过期键值对;AOF写入文件时,对过期的key会追加一条del命令,当执行AOF重写时,会忽略过期key和del命令
网友评论