持久化
.
RDB
RDB持久化是把当前进程数据生成快照保存到硬盘的过程,触发RDB持久化过程分为手动触发和自动触发
手动触发
save命令
阻塞当前Redis服务器,知道RDB过程完成为止,对于内存比较大的实例会造成长时间阻塞,因此这个方法基本不采用
bgsave命令
Redis进程执行fork操作创建子进程,RDB持久化过程由子进程负责,完成后自动结束,阻塞只发生在fork阶段,一般时间很短,因此,Redis内部的所有设计RDB的操作都采用类似bgsave的方式
自动触发
- 使用save配置,如"save m n"表示m秒内数据集发生了n次修改之后,就会自动触发RDB持久化
- 从节点进行全量复制操作时,主节点自动进行RDB持久化,随后将RDB文件内容发送给从节点
- 执行shutdown命令关闭Redis时,执行RDB持久化
bgsave的具体流程
- 执行bgsave命令,Redis父进程判断当前进程是否存在其他正在执行的子进程,如RDB/AOF子进程,如果存在bgsave命令直接返回.
- 父进程执行fork创建子进程,fork过程中父进程会阻塞,通过info stats命令查看latest_fork_usec选项,可以获取最近一次fork操作的耗时,单位为微秒
- 父进程fork完成之后,bgsave命令会返回"Background saving started"信息并不再阻塞父进程,可以继续响应其他命令.
- 子进程创建RDB文件,根据父进程内存生成的临时快照文件,完成后对原有文件进行原子替换,执行lastssave命令可以获取最后一次生成RDB的时间,对应info统计的rdb_last_save_time选项
- 子进程发送信号给父进程表示完成,父进程更新统计信息
RDB的处理
保存
RDB 文件保存再 dir 配置指定的目录(默认 /var/lib/redis/)下,文件名通过 dbfilename
配置(默认 dump.rdb)指定。可以通过执行 config set dir {newDir} 和 config set dbfilename
{newFilename} 运行期间动态执行,当下次运行时 RDB 文件会保存到新目录。
压缩
Redis默认采用LZF算法对生成的RDB文件做牙压缩处理,压缩后的文件远远小于内存大小,默认开启,可以通过参数config set rdbcompression {yes|no} 动态修改.
虽然压缩RDB会消耗CPU,但可以大幅度降低文件的体积,方便保存在硬盘或通过网络发送到从节点,因此建议开启
校验
如果Redis启动时加载到损坏的RDB文件会拒绝启动,这时可以使用Redis提供的redis-dump工具检测RDB文件并获取对应的错误报告
RDB的优缺点
- RDB是一个紧凑压缩的二进制文件,代表Redis在某个时间点上的数据快照,非常适用于备份,全量复制等场景,比如每6个小时执行bgsave备份,并把RDB文件复制到远程机器或者文件系统中用于灾备
- Redis加载RDB恢复数据远远快于AOF的方式
- RDB方式数据没办法做到实时持久化/秒级持久化,因为bgsave每次运行都要执行fork创建子进程,属于重量级操作,频繁执行成本过高.
- RDB文件使用特定二进制格式保存,Redis版本演进过程中有多个RDB版本,兼容性可能会有风险
AOF
AOF持久化,是以独立日志的方式记录每条写命令,重启时再重新执行AOF文件中的命令来达到恢复数据的目的.AOF的主要作用就是解决了数据持久化的实时性,目前已经是Redis持久化的主流方式
AOF的工作流程如下:
命令写入->文件同步->文件重写->重启加载
- 所有的写入命令会追加到AOF缓冲区中
- AOF缓冲区根据对应的策略向AOF文件(硬盘)做同步操作
- 随着AOF文件越来越大,需要定期对AOF文件进行重写,达到压缩的目的
- 当redis服务器启动时,就可以加载AOF文件里的命令进行数据恢复
命令写入
AOF命令写入的内容格式是文本协议格式,因为这样更具备较好的兼容性,可读性,更简单
为什么要使用AOF缓冲区:
redis是单线程执行的,如果每次进行命令写入都直接与硬盘交互,就从对内存的读写变为了对硬盘IO的读写,性能就必然会严重下降,而采用缓冲区过后,减少了IO的次数
文件同步
Redis提供了多种AOF缓冲区同步文件策略,由参数appendfsync控制
可能配置 | 说明 |
---|---|
always | 命令写⼊ aof_buf 后调用 fsync 同步,完成后返回 |
everysec | 命令写⼊aof_buf 后只执行 write 操作,不进行fsync。每秒由同步线程进行 fsync |
no | 命令写⼊ aof_buf 后只执行 write 操作,由 OS 控制fsync 频率。 |
系统调用wirte和fsync说明
• write 操作会触发延迟写(delayed write)机制。Linux 在内核提供页缓冲区用来提供硬盘 IO 性能。write 操作在写入系统缓冲区后立即返回。同步硬盘操作依赖于系统调度机制,例如:缓冲区页空间写满或达到特定时间周期。
同步文件之前,如果此时系统故障宕机,缓冲区内数据将丢失。
• Fsync 针对单个文件操作,做强制硬盘同步,fsync 将阻塞直到数据写入到硬盘。
• 配置为 always 时,每次写入都要同步 AOF 文件,性能很差,在⼀般的 SATA 硬盘上,只能支持大约几百 TPS 写入。除非是非常重要的数据,否则不建议配置。
• 配置为 no 时,由于操作系统同步策略不可控,虽然提高了性能,但数据丢失风险大增,除非数据重要程度很低,一般不建议配置。
• 配置为 everysec,是默认配置,也是推荐配置,兼顾了数据安全性和性能。理论上最多丢失 1 秒的数据。
重写机制
随着命令不断写入AOF,文件会越来越大,为了解决这个问题,Redis引入AOF重写机制压缩文件体积,AOF文件重写是把Redis进程内数据转化为命令同步到新的AOF文件
重写后的AOF为什么可以变小?
- 进程内已超时的数据不再写入文件
- 旧的AOF中的无效命令,只需保留数据的最终版本
- 多条写操作合并为一条
AOF重写过程可以手动触发和自动触发
手动触发: 调用bgrewriteaof命令
自动触发: 根据auto-aof-rewrite-min-size 和 auto-aof-rewrite-percentage 参数确定自动触发时机
- 执行 AOF 重写请求。
如果当前进程正在执行 AOF 重写,请求不执行。如果当前进程正在执行bgsave 操作,重写命令延迟到 bgsave 完成之后再执行。 - 父进程执行fork 创建子进程。
- 重写
a. 主进程 fork 之后,继续响应其他命令。所有修改操作写入 AOF 缓冲区并根据 appendfsync 策略同步到硬盘,保证旧 AOF 文件机制正确。
b. 子进程只有 fork 之前的所有内存信息,父进程中需要将 fork 之后这段时间的修改操作写入AOF 重写缓冲区中。 - 子进程根据内存快照,将命令合并到新的 AOF文件中。
- 子进程完成重写
a. 新文件写入后,子进程发送信号给父进程。
b. 父进程把 AOF重写缓冲区内临时保存的命令追加到新 AOF 文件中。
c. ⽤新 AOF 文件替换老AOF 文件。
启动时恢复数据
本章重点回顾
- Redis 提供了两种持久化方案:RDB 和 AOF。
- RDB 视为内存的快照,产生的内容更为紧凑,占用空间较小,恢复时速度更快。但产生 RDB 的开
销较大,不适合进行实时持久化,⼀般用于灾备和主从复制。 - AOF 视为对修改命令保存,在恢复时需要重放命令。并且有重写机制来定期压缩 AOF文件。
- RDB 和 AOF 都使用fork 创建子进程,利用 Linux 子进程拥有父进程内存快照的特点进行持久化,
尽可能不影响主进程继续处理后续命令。