Redis 笔记之七:持久化
持久化可以避免进程退出而数据丢失。
Redis 的持久化文件主要包括两种格式,RDB 格式和 AOF 格式,这两种格式的生成机制各有不同。
Redis 在重启时优先加载 AOF 文件(因为 AOF 文件颗粒度更细),如果没有 AOF 文件可加载则加载 RDB 文件。
RDB
RDB 持久化会把进程内的数据全量快照保存到硬盘上,其触发方式包括手动触发和自动触发。
手动触发
1 |
|
bgsave 的工作流程如下:
注意,RDB 文件本身永远只有一个。子进程产生的都是临时文件,会通过原子替换的方式来维持 RDB 文件的唯一性。
Redis 内部主动生成 RDB 文件的过程都是采用 bgsave 的方式。
自动触发
- 在 config 里配置
save m n
。表示 m 内数据集存在 n 次修改时,自动触发 bgsave。 - 如果从节点执行全量复制操作,主节点自动执行 bgsave生成 RDB 文件并发送给从节点。
- 执行 debug reload 命令重新加载 Redis 时,也自动触发 save 操作。
- 默认情况下执行 shutdown 命令时,如果没有开启 AOF 持久化功能,则自动执行 bgsave-Redis 自带的优雅关闭流程。
RDB 文件的存储
默认会存在 config set dir {newDir}
和config set dbfilename {newFileName}
的文件里。也可以动态修改()比如在硬盘出问题的时候动态修改)。
默认情况下会对 RDB 文件进行 LZF 压缩处理(可以关闭)。压缩会消耗 CPU 性能,但利于网络传输。
RDB 的优缺点
优点:
- 紧凑存储,适于全量保存/恢复。
- 加载 RDB 的速度远快于 AOF 文件。
缺点:
- 没有办法强实时/秒级持久化。
- 版本不兼容
AOF(append only file)
以独立的日志(log)记录每次写命令,重启时再重新执行 AOF 文件中的命令达到恢复数据的目的。AOF 具有持久化的强实时性,是 RDB 的补充。
配置方式
需要设置appendonly yes
(默认不开启),开启后就会被自动触发(也就是说无需手动触发)。可以修改文件名和路径。
工作流程
aof 的工作流程如下:
其中:
- append 其实是把写命令写入 aof_buf 中。
- sync 是把写入命令从内存持久化到硬盘中。
- rewrite 则会压缩重写 AOF 文件使其达到更紧凑的目的。
- 当 Redis 重启后,可以加载 AOF 文件进行数据恢复。
其中被持久化的命令,是纯文本的格式,遵循 RESP 协议。
文件同步
Redis 本身是个单线程架构,如果每次新的写命令都要写入硬盘,则硬盘的负载能力可能成为一个瓶颈。而如果先写入一个 aof_buf,则可以采取多种策略持久化数据到硬盘中。
这提醒我们,对于零散的多次写 IO,我们都要想办法化零为整,通过 buffer-sync 的分段异步写形式充分协调内存和硬盘的写性能。
我们可以采用的策略分别是:
- always 命令写入 aof_buf 后调用系统 fsync 操作同步到 AOF 文件,fsync 完成后系统返回。等于一个强实时的策略。在 sata 上只有几百的 tps。
- everysec 命令写入 aof_buf 后调用系统 write 操作,write 完成后线程返回。fsync 操作由专门线程每秒调用一次。等于一个中等实时的策略。默认策略,最多丢失 1s 的数据
- no 命令写入 aof_buf后调用系统 write 操作,而不对 AOF 做 fsync 同步操作, fsync 同步操作由操作系统负责,通常同步周期最长 30 秒。无法保障同步实时性。
write 其实是一种 delaywrite,写入操作系统的页缓冲区以后线程会立即返回(阻塞期很短),依赖于系统的调度(如时间阈值满或者空间阈值满,期间如果系统宕机则数据会丢失)或者 fsync才能真正持久化到硬盘中。
fsync 就是强行同步到硬盘的syscall,阻塞时间会稍长,但持久化更彻底。
重写机制
重写主要通过有效地去除无用命令、合并多条有效命令的方式使得 AOF 文件变得更小,更利于加载。
重写(只是重写这个过程)也同样包括手动触发和被动触发两种机制。
重写流程:
其中同时存在两个 aof 文件,新的 aof 文件也会进行一个原子替换。
手动触发
1 |
|
自动触发
if (aof_current_size > auto-aof-rewrite-min-size && (aof_current_size - aof_base_size)/aof_base_size >= auto-aof-rewrite-percentage)
auto-aof-rewrite-min-size 和 auto-aof-rewrite-percentage 是配置文件的配置项。
相关问题
fork 问题
fork 调用会让子进程调用父进程的内存页表,理论上一个进程占用内存越大,它的 fork 命令就越耗时,而且有些虚拟机(如 xen)里 fork 的性能还更差。
性能优化点
cpu
- 不要和其他 cpu 密集型应用部署在一起。
- 如果部署多个 Redis 实例,保证每个时刻只有一个后台进程在工作。
内存
- 如果部署多个 Redis 实例,保证每个时刻只有一个后台进程在工作。
- 不要增加 copy-on-write 的负担,尽可能让子进程少一些后台重写的负担(如何做到?)。
- 关掉大页(huge page)优化-和 JVM 的优化策略正好相反。
硬盘
AOF 对硬盘的写压力更大。
AOF 追加阻塞
此外,AOF 体系还存在一个追加阻塞的问题,其流程如下:
可以看到,主线程如果在写入 AOF 缓冲区时如果上次 fsync 成功的时间(所谓的 checkpoint)还在两秒内,则会持续写入 AOF 缓冲区。这时候 AOF 缓冲区是不安全的。所以实际上 AOF 机制最多会丢两秒内的信息。而如果主线程阻塞,则 Redis 的性能会急剧下降。
至此为止,我们至少有三种潜在的性能瓶颈:save、fork 和 AOF 的 append 造成的阻塞。