Redis的持久化之RDB和AOF
什么是Redis的持久化?
Redis的热点数据是存放在缓存中,为了防止设备突然宕机,造成数据的丢失。持久化就是把内存中的热点数据存放到磁盘中去。下次启动时直接从磁盘中读取数据,这样就保证数据的持久化。
为什么要持久化?
防止数据意外丢失,确保数据安全性。
一、Reids的持久化
redis提供了不同级别的持久化方式:
- RDB持久化方式能够在指定的时间间隔内对你的数据进行快照存储。存储简单,关注点在数据。
- AOF持久化方式记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令恢复原始的数据,AOF命令以redis协议追加保存每次写的操作到文件末尾,Redis还支持对AOF文件进行后台重写,使得AOF文件的体积不至于过大。主要是通过将数据的操作过程进行保存,日志形式,存储操作过程,存储格式复杂,关注点在数据的操作过程。
具体使用哪种持久化策略?
- 如果数据不能丢失,RDB和AOF混用。
- 如果只作为缓存使用,可以接受部分数据的丢失,可以使用RDB。
- 如果只使用AOF,优先使用everysec的写回策略。
二、RDB持久化
- RDB 持久化是指在指定的时间间隔内将内存中当前进程中的数据生成快照保存到硬盘(因此也称作快照持久化),用二进制压缩存储,保存的文件后缀是 rdb
- 当 Redis 重新启动时,可以读取快照文件恢复数据
1、触发条件
RDB持久化的触发为手动触发和自动触发两种。
1.1 手动触发
- save命令和bgsave命令都可以生成RDB文件。
- save命令会阻塞Redis服务器进程,直到RDB文件创建完毕为止,在Rdis服务器阻塞期间,服务器不能处理任何命令请求。
- 而bgsave命令会创建一个子进程,由子进程来负责创建RDB文件,父进程(即redis主进程)则继续处理请求。
- bgsave命令执行过程中,只有fork子进程时会阻塞服务器,而对于save命令,整个过程中会阻塞服务器,因此save已基本被废弃,线上环境要杜绝使用save的使用。
1.2 自动触发
在自动触发RDB持久时,Redis也会选择bgsave而不是save来进行持久化。
save m n
#自动触发最常见的情况是在配置文件中通过 savemn,指定当 m 秒内发生 n 次变化时,会触发 bgsave
vim /etc/redis/6379.conf
#219行,以下三个save条件满足任意一个时,都会引起bgsave的调用
save 900 1 :当时间到900秒时,如果redis数据发生了至少1次变化,则执行bgsave
save 300 10 :当时间到300秒时, 如果redis数据发生了至少10次变化,则执行bgsave
save 60 10000 :当时间到60秒时,如果redis数据发生了至少10000次变化, 则执行bgsave
#254行,指定 RDB 文件名
dbfilename dump.rdb
#264行,指定 RDB 文件和 AOF 文件所在目录
dir /var/lib/redis/6379
#242行,是否开启 RDB 文件压缩
rdbcompression yes
其他自动触发机制除了savemn以外,还有一些其他情况会触发bgsave:
- 在主从复制场景下,如果从节点执行全量复制操作,则主节点会执行bgsave命令,并将rdb文件发送给从节点。
- 执行shutdown命令时,自动执行rdb持久化。
- redis被kill则不会触发备份机制。
2、执行流程
- Redis父进程首先判断:当前是否哦在执行save,或bgsave/bgrewriteaof的子进程,如果在执行,则bgsave命令直接返回,bgsave/bgrewriteaof的子进程不能同时执行;主要是基于性能方面的考虑:两个并发的子进程同时执行大量的磁盘写操作,可能引起严重的性能问题。
- 父进程执行fork操作创建子进程,这个过程中父进程是阻塞的,Redis不能执行来自客户端的任务命令。
- 父进程fork后,bgsave命令返回“Background saving started”信息并不再阻塞父进程,并可以响应其他命令。
- 子进程创建RDB文件, 根据父进程内存快照生成临时快照文件,完成后对原有文件进行原子替换。
- 子进程发送信号给父进程表示完成,父进程更新统计信息。
3、启动时加载
- RDB文件的载入工作是在服务器启动时自动执行的,并没有专门的命令。
- 但是由于AOF的优先级更高,因此AOF开启时,Redis会优先载入AOF文件来恢复数据
- 只有当AOF关闭时,才会在Redis服务器启动时检测RDB文件,并自动载入
- 服务器载入RDB文件期间处于阻塞状态,直至载入完成为止
- Redis载入RDB文件时,会对RDB文件进行校验,如果文件损坏,则日志中会打印错误(Redis启动失败)
三、AOF持久化
什么是AOF?
AOF 持久化记录服务器执行的所有写操作命令,并在服务器启动时,通过重新执行这些命令来还原数据集。AOF 文件中的命令全部以 Redis 协议的格式来保存,新命令会被追加到文件的末尾。 Redis 还可以在后台对 AOF 文件进行重写(rewrite),使得 AOF 文件的体积不会超出保存数据集状态所需的实际大小。
AOF特点:
- RDB持久化是将进仓数据写入文件,而AOF持久化,则是将Redis执行的每次写、删除命令记录到单独的日志文件中,查询操作不会记录。
- 当Redis重启时再次执行AOF文件中的命令来恢复数据。
- 与RDB相比,AOF的实时更好,因此已成为主流的持久化方案。
1、开启AOF
Redis服务器默认开启RDB,关闭AOF
要开启AOF,需要在配置文件中配置:
vim /etc/redis/6379.conf
#700行修改, 开启AOF
appendonly yes
#704行,指定AOF文件名称
appendfilename "appendonly.aof"
#796行,是否忽略最后一条可能存在问题的指令
aof-load-truncated yes
/etc/init.d/redis_6379 restart
#重启以使配置生效,开启 AOF
2、执行流程
- 由于需要记录Redis的每条写命令,因此AOF不需要触发,下面介绍AOF的执行流程。
- AOF的执行流程包括:
- 命令追加(append):将Redis的写命令追加到缓冲区aof_buf;
- 文件写入(write)和文件同步(sync):根据不同的同步策略将aof_buf中的内容同步到硬盘;
- 文件重写(rewrite):定期重写AOF文件,达到压缩的目的。
3、命令追加(append)
redis先将写命令追加到缓冲区,而不是直接写入文件,主要是为了避免每次有写命令都直接写入硬盘,导致硬盘IO成为redis负载的瓶颈。
命令追加的格式是redis命令请求的协议格式,它是一个纯文本格式,具有兼容性好、可读性强、容易处理、操作简单避免二次开销等优点。
在AOF文件中,除了用于指定数据库的select命令(如select0为选中0号数据库)是由redis添加的,其他都是客户端发送来的
4、文件写入(write)和文件同步(sync)
4.1 redis提供了多种AOF缓冲区的同步文件策略
策略涉及到操作系统的write函数和fsync函数,说明如下:
为了提高文件写入效率,在现代操作系统中,当用户调用write函数讲数据写入文件时,操作系统通常会讲数据缓存到一个内存缓冲区里,当缓冲区被填满后或超过指定时限后,才真正讲缓冲区的数据i写入到硬盘里。
这样的操作虽然提高了效率,但也带来了安全问题:如果计算机停机,内存缓冲区中的数据会丢失。
因此系统同时提供了fsync、fdatasync等同步函数,可以强制操作系统立刻将缓冲区中的数据写入到硬盘里,从而确保数据的安全性。
4.2 AOF缓冲区的同步文件策略存在三种同步方式
always:命令写入aof_buf后立即调用系统fsync操作同步到AOF文件,fsync完成后线程返回,always就是每次写命令都要同步到AOF文件,硬盘IO成为性能瓶颈,redis只能支持大约几百TPS写入,严重降低了redis的性能,即便是使用固态硬盘(SSD),每秒大约也只能处理几万个命令,而且会大大降低磁盘使用寿命。
no:命令写入ao_buf后调用系统write操作,不对AOF文件做fsync同步;同步操作系统负责,通常同步周期为30秒。这种情况下,文件同步的时间不可控,且缓冲区堆积的数据会很多,数据完全性无法保证。
eveysec:命令写入aof_buf后调用系统write操作,write完成后线程返回;fsync同步文件操作有专门的线程每秒调用一次。everysec是前面两个模式的折中模式,是性能和数据安全性的平衡,因此是redis的默认配置,也是最常用的模式。
5、文件重写(rewrite)
什么是文件重写?
文件重写是指定定期重写AOF文件,减少AOF文件的大小,但是需要注意的是,AOF重写是redis进程内的数据转换为命令,同步到新的AOF文件,而不会对旧iAOF文件进行任何读取、写入操作。
为什么要文件重写?
随着长时间的运行,redis服务器执行的写命令会越来越多,AOF文件也会越来越大,而过大的AOF文件不仅会影响服务器的正常运行,也会导致数据恢复的时间变长。可以通过文件重写降低AOF文件大小。
关于文件重写需要注意的是:对于AOF持久化来说,文件重写虽然是强烈推荐的,但并不是必须的,即使没有文件重写,数据也可以被持久化并在redis启动的时候导入,因此在一些现实中,会关闭自动的文件重写,然后通过定时任务来完成数据的更新。
文件重写能够压缩AOF文件的原因
过期的数据不再写入文件
无效的命令不再写入文件:如有数据被重复设置、有些数据被删除了等。
多条命令可以合并为一个:如sadd myset v1, sadd myset v2, sadd myset v3可以合并为 sadd myset v1 v2 v3
通过上述内容可以看出,由于重写后AOF执行的命令减少了,文件重写既可以减少文件占用的空间,也可以提高恢复速度。
文件重写流程
(1):Redis 父进程首先判断当前是否存在正在执行 bgsave/bgrewriteaof 的子进程,如果存在则 bgrewriteaof 命令直接返回,如果存在 bgsave 命令则等 bgsave 执行完成后再执行
(2):父进程执行fork操作创建子进程,这个过程中父进程是阻塞的
(3.1):父进程 fork 后,bgrewriteaof 命令返回"Background append only file rewrite started"信息并不再阻塞父进程,并可以响应其他命令;Redis 的所有写命令依然写入 AOF 缓冲区,并根据 appendfsync 策略同步到硬盘,保证原有 AOF 机制的正确
(3.2):由于 fork 操作使用写时复制技术,子进程只能共享 fork 操作时的内存数据;由于父进程依然在响应命令,因此 Redis 使用 AOF 重写缓冲区(aof_ rewrite_buf)保存这部分数据,防止新 AOF 文件生成期间丢失这部分数据;也就是说,bgrewriteaof 执行期间,Redis 的写命令同时追加到 aof_ buf 和 aof_ rewirte_ buf 两个缓冲区
(4):子进程根据内存快照,按照命令合并规则写入到新的 AOF 文件
(5.1):子进程写完新的 AOF 文件后,向父进程发信号,父进程更新统计信息,具体可以通过 info persistence 查看
(5.2):父进程把 AOF 重写缓冲区的数据写入到新的 AOF 文件,这样就保证了新AOF文件所保存的数据库状态和服务器当前状态一致
(5.3):使用新的 AOF 文件替换老文件,完成 AOF 重写
文件重写的触发
分手动触发和自动触发
手动触发:
- 直接调用 bgrewriteaof 命令,该命令的执行与 bgsave 有些类似
- 都是 fork 子进程进行具体的工作,且都只有在fork时阻塞
自动触发:
- 通过设置 auto-aof-rewrite-min-size 选项和 auto-aof-rewrite-percentage 选项来自动执行BGREWRITEAOF
- 只有当 auto-aof-rewrite-min-size 和 auto-aof-rewrite-percentage 两个选项同时满足时,才会自动触发AOF重写,即 bgrewriteaof 操作
vim /etc/redis/ 6379. conf
#771行
auto-aof- rewrite-percentage 100
#当前 AOF 文件大小(即 aof_current_size)是上次日志重写时AOF文件大小(aof_base_size)两倍时,发生 BGREWRITEAOF操作
auto-aof -rewrite-min-size 64mb
#当前 AOF文件执行 BGREWRITEAOF 命令的最小值
#避免刚开始启动 Reids 时由于文件尺寸较小导致频繁的 BGREWRITEAOF
启动时加载
- 当 AOF 开启时,Redis 启动时会优先载入 AOF 文件来恢复数据
- 只有当 AOF 关闭时,才会载入 RDB 文件恢复数据
- 当 AOF 开启,但 AOF 文件不存在时,即使 RDB 文件存在也不会加载
- Redis 载入 AOF 文件时,会对 AOF 文件进行校验,如果文件损坏,则日志中会打印错误,Redis 启动失败
- 但如果是 AOF 文件结尾不完整 (机器突然宕机等容易导致文件尾部不完整),且 aof-load- truncated 参数开启,则日志中会输出警告,Redis 忽略掉AOF文件的尾部,启动成功
- aof-load-truncated 参数默认是开启的
四、总结
具体使用哪种持久化策略?
- 如果数据不能丢失,RDB和AOF混用。
- 如果只作为缓存使用,可以接受部分数据的丢失,可以使用RDB。
- 如果只使用AOF,优先使用everysec的写回策略。
宕机等容易导致文件尾部不完整),且 aof-load- truncated 参数开启,则日志中会输出警告,Redis 忽略掉AOF文件的尾部,启动成功
- aof-load-truncated 参数默认是开启的
四、总结
具体使用哪种持久化策略?
- 如果数据不能丢失,RDB和AOF混用。
- 如果只作为缓存使用,可以接受部分数据的丢失,可以使用RDB。
- 如果只使用AOF,优先使用everysec的写回策略。