RDB 持久化是把当前进程数据生成快照保存到硬盘的过程。 触发 RDB 持久化过程分为手动触发和自动触发。RDB 完成后会自动生成一个文件,保存在 dir 配置的指定目录下,文件名是 dbfileName 指定。

RDB 文件是经过压缩的二进制文件,存储路径既可以在启动前配置,也可以通过命令动态设定。动态设定存储路径在磁盘损害或空间不足时非常有用,命令为:config set dir {newdir} 和 config set dbfilename {newFileName}。

Redis 默认会采用 LZF 算法对生成的 RDB 文件做压缩处理,压缩后的文件远远小于内存大小,默认开启。

RDB 文件在 Redis 启动时自动载入,没有专门的命令。但是由于 AOF 的优先级更高,因此当 AOF 开启时,Redis 会优先载入 AOF 文件来恢复数据。只有当 AOF 关闭时,才会检测 RDB 文件,并自动载入。服务器载入 RDB 文件期间处于阻塞状态,直到载入完成为止。同时,在载入 RDB 文件的过程中会对 RDB 文件进行校验,如果文件损坏,则日志中会打印错误,Redis 启动失败。

RDB 的优点:

RDB 是一个紧凑压缩的二进制文件,代表 Redis 在某个时间点上的数据快照。非常适用于备份,全量复制等场景。比如每 6 小时执行 bgsave 备份,并把 RDB 文件拷贝到远程机器或者文件系统中,用于灾难恢复。
Redis 加载 RDB 恢复数据远远快于 AOF 的方式。
RDB 的缺点:

RDB 方式数据没办法做到实时持久化/秒级持久化。因为 bgsave 每次运行都要执行 fork 操作创建子进程,属于重量级操作,频繁执行成本过高。
RDB 文件使用特定二进制格式保存, Redis 版本演进过程中有多个格式的 RDB 版本, 存在老版本 Redis 服务无法兼容新版 RDB 格式的问题。
触发 RDB 持久化
手动触发:命令有 save 和 bgsave

save:该命令会阻塞 Redis 服务器,直到 RDB 的过程完成,已经被废弃,因此线上不建议使用。
bgsave:每次进行 RDB 进程都会 fork 一个子进程,由子进程完成 RDB 的操作,因此阻塞只会发生在 fork 阶段,一般时间很短。
NOTE:因为 save 命令的整个过程都会阻塞服务器,因此已经被废弃,线上环境要杜绝 save 命令的使用。

自动触发的场景:

根据配置项 save m n 自动触发,指定当 m 秒内发生 n 次变化时,会触发 bgsave。
在主从复制场景下,如果 SLAVE 执行全量复制操作,则 MASTER 会执行 bgsave 命令,并将 RDB 文件发送给 SLAVE。
执行 debug reload 命令重新加载 Redis 时, 也会自动触发 bgsave 操作。
默认情况下执行 shutdown 命令时, 如果没有开启 AOF 持久化功能则自动执行 bgsave。
RDB 执行流程
在这里插入图片描述

执行 bgsave 命令后,会先判断是否存在 AOF 或者 RDB 的子进程,如果存在,直接返回。
父进程 fork 操作创建一个子进程,fork 操作中父进程会被阻塞。
fork 完成后,子进程开始根据父进程的内存生成临时快照文件,完成后对原有的 RDB 文件进行替换。执行 lastsave 命令可以查看最近一次的 RDB 时间。
子进程创建RDB文件,根据父进程内存快照生成临时快照文件,完成后对原有文件进行原子替换。
子进程完成后发送信号给父进程,父进程更新统计信息。
RDB 常用配置
save m n:bgsave 自动触发的条件。如果没有 save m n 配置,相当于自动的 RDB 持久化关闭,不过此时仍可以通过其他方式触发。
stop-writes-on-bgsave-error yes:当 bgsave 出现错误时,Redis 是否停止执行写命令;
设置为 yes,则当硬盘出现问题时,可以及时发现,避免数据的大量丢失;
设置为 no,则 Redis 无视 bgsave 的错误继续执行写命令,当对 Redis 服务器的操作系统(尤其是硬盘)使用了监控时,该选项考虑设置为 no。
rdbcompression yes:是否开启 RDB 文件压缩。
rdbchecksum yes:是否开启 RDB 文件的校验,在写入文件和读取文件时都起作用。关闭 checksum 在写入文件和启动文件时大约能带来 10% 的性能提升,但是数据损坏时无法发现。
dbfilename dump.rdb:RDB 文件名。
dir ./:RDB 文件和 AOF 文件所在目录。
AOF
AOF(Append Only File)持久化,以独立日志的方式记录每次写命令,即:每次写命令都会被记录到单独的日志文件中,重启时再重新执行 AOF 文件中的命令达到恢复数据的目的。AOF 的主要作用是解决了数据持久化的实时性, 目前已经是 Redis 持久化的主流方式。

Redis 服务器默认开启 RDB,关闭 AOF。要开启 AOF,需要在配置文件中配置:appendonly yes。AOF 文件名通过 appendfilename 配置设置, 默认文件名是 appendonly.aof。保存路径同 RDB 持久化方式一致,通过 dir 配置指定。

AOF 执行流程
由于 AOF 会记录 Redis 的每条写命令,因此 AOF 不需要设置触发条件。

与载入 RDB 文件类似,Redis 启动时载入 AOF 文件,也会进行校验,如果文件损坏,则日志中会打印错误,Redis 启动失败。但如果是 AOF 文件结尾不完整,例如:机器突然宕机等容易导致文件尾部不完整,且 aof-load-truncated(默认是开启的)参数开启,则日志中会输出警告,Redis 忽略掉 AOF 文件的尾部,启动成功。

注意:因为 Redis 的命令只能在客户端上下文中执行,而载入 AO F文件时命令是直接从文件中读取的,并不是由客户端发送。因此 Redis 服务器在载入 AOF 文件之前,会先创建一个没有网络连接的客户端(伪客户端),之后用它来执行 AOF 文件中的命令,命令执行的效果与带网络连接的客户端完全一样。

AOF 整体的执行流程分为 4 个步骤:

命令写入(追加,Append):将 Redis 的写命令追加到缓冲区 aof_buf。
文件同步(Sync):根据不同的同步策略将 aof_buf 中的内容同步到硬盘;
文件重写(Rewrite):定期重写 AOF 文件,达到压缩的目的。
重启加载
在这里插入图片描述

命令写入
AOF 命令写入,又称命令追加,内容直接是文本协议格式。例如 set hello world 这条命令, 在 AOF 缓冲区会追加如下文本:

*3\r\n$3\r\nset\r\n$5\r\nhello\r\n$5\r\nworld\r\n
需要注意的是,Redis 先将写命令追加到缓冲区,而不是直接写入文件系统,主要是为了避免每次有写命令都直接写入硬盘,导致硬盘 IO 成为 Redis 负载的瓶颈。这是大多数高性能数据库的常规设计。

Redis 使用单线程响应命令,如果每次写 AOF 文件命令都直接追加到硬盘, 那么性能完全取决于当前硬盘负载。先写入缓冲区 aof_buf 中, 还有另一个好处, Redis 可以提供多种缓冲区同步硬盘的策略,在性能和安全性方面做出平衡。

文件同步
Redis 提供了多种 AOF 缓存区的文件同步策略,策略涉及到操作系统的 write 函数和 fsync 函数:为了提高文件写入效率,在现代操作系统中,当用户调用 write 函数将数据写入文件时,操作系统通常会将数据暂存到一个内存缓冲区里,当缓冲区被填满或超过了指定时限后,才真正将缓冲区的数据写入到硬盘里。这样的操作虽然提高了效率,但也带来了安全问题,例如:如果计算机停机,内存缓冲区中的数据会丢失。因此系统同时提供了 fsync、fdatasync 等同步函数,可以强制操作系统立刻将缓冲区中的数据写入到硬盘里,从而确保数据的安全性。

AOF 缓存区的同步文件策略由配置项 appendfsync 控制,具有以下参数类型:

always:每次写入都要同步 AOF 文件,即:命令写入 aof_buf 后立即调用系统 fsync 函数操作同步到 AOF 文件,fsync 完成后线程返回。这种情况下,每次有写命令都要同步到 AOF 文件,硬盘 IO 成为了性能瓶颈。在一般的 SATA 硬盘上,Redis 只能支持大约几百 TPS 写入,即便是 SSD 固态硬盘,每秒大约也只能处理几万个命令,而且会大大降低 SSD 的寿命。 显然跟 Redis 高性能特性背道而驰,不建议配置。
no:命令写入 aof_buf 后调用系统 write 函数操作,因为不对 AOF 文件做 fsync 同步,而是由操作系统负责,同步周期通常为 30 秒,但这种同步的时间是不可控的,且缓冲区中堆积的数据会很多,数据安全性无法保证。虽然提升了性能,但数据安全性无法保证。
everysec(默认),建议使用。命令写入 aof_buf 后调用系统 write 操作,write 完成后线程返回。fsync 同步文件操作由专门的线程每秒调用一次。是前述两种策略的折中,是性能和数据安全性的平衡。
做到兼顾性能和数据安全性。理论上只有在系统突然宕机的情况下丢失 1 秒的数据。

文件重写
为什么要文件重写呢? 因为过大的 AOF 文件不仅会影响服务器的正常运行,也会导致数据恢复需要的时间过长。文件重写能够使得 AOF 文件的体积变得更小,从而使得可以更快的被 Redis 加载。

文件重写是指定期重写 AOF 文件,减小 AOF 文件的体积。需要注意的是,AOF 重写是把 Redis 进程内的数据转化为写命令,同步到新的 AOF 文件,不会对旧的 AOF 文件进行任何读取、写入操作。

值得注意的是,文件重写虽然是强烈推荐的,但并不是必须的。即使没有文件重写,数据也可以被持久化并在 Redis 启动的时候导入。因此在一些场景中,会关闭自动的文件重写,然后通过定时任务在每天的某一时刻定时执行。

文件重写之所以能够压缩 AOF 文件,是基于 3 个现实前提:

过期的数据不再写入文件。
无效的命令不再写入文件。
多条命令可以合并为一个。
重写过程分为手动触发和自动触发:

手动触发:直接使用 bgrewriteaof 命令,fork 子进程进行具体的工作,父进程仅在 fork 时被阻塞。
自动触发:根据 auto-aof-rewrite-min-size 和 auto-aof-rewrite-percentage 参数确定自动触发时机。
auto-aof-rewrite-min-size:表示运行 AOF 重写时文件最小体积, 默认为 64MB。
auto-aof-rewrite-percentage:代表当前 AOF 文件空间(aof_current_size) 和上一次重写后 AOF 文件空间(aof_base_size) 的比值。
自动触发时机相当于:

aof_current_size > auto-aof-rewrite-minsize && (aof_current_size - aof_base_size) / aof_base_size >= auto-aof-rewritepercentage
其中,aof_current_size 和 aof_base_size 可以在 info Persistence 统计信息中查看到。

注意,只有当 auto-aof-rewrite-min-size 和 auto-aof-rewrite-percentage 两个参数同时满足时,才会自动触发 AOF 重写,即 bgrewriteaof 命令操作。

Redis 内部进行文件重写的流程:
在这里插入图片描述

重写期间,主线程并没有阻塞,而是在执行其他的操作命令,依然会向旧的 AOF 文件写入数据,这样能够保证备份的最终完整性,如果数据重写失败,也能保证数据不会丢失。
为了把重写期间响应的写入信息也写入到新的文件中,因此也会为子进程保留一个缓冲区,防止新写的文件丢失数据。
重写是直接把当前内存的数据生成对应命令,并不需要读取老的 AOF 文件进行分析、命令合并。
AOF 文件直接采用的文本协议,主要是兼容性好、追加方便、可读性高可认为修改修复。
无论是 RDB 还是 AOF 都是先写入一个临时文件,然后通过重命名完成文件的替换。
AOF 的优点:使用 AOF 持久化会让 Redis 变得非常耐久:你可以设置不同的 fsync 策略,比如无 fsync ,每秒钟一次 fsync ,或者每次执行写入命令时 fsync 。AOF 的默认策略为每秒钟 fsync 一次,在这种配置下,Redis 仍然可以保持良好的性能,并且就算发生故障停机,也最多只会丢失一秒钟的数据(fsync 会在后台线程执行,所以主线程可以继续努力地处理命令请求)。

AOF 的缺点:

对于相同的数据集来说,AOF 文件的体积通常要大于 RDB 文件的体积。根据所使用的 fsync 策略,AOF 的速度可能会慢于 RDB。在一般情况下, 每秒 fsync 的性能依然非常高, 而关闭 fsync 可以让 AOF 的速度和 RDB 一样快, 即使在高负荷之下也是如此。不过在处理巨大的写入载入时,RDB 可以提供更有保证的最大延迟时间。
数据恢复速度相对于 RDB 比较慢。
重启加载
无论是 RDB 还是 AOF 都可用于服务器重启时的数据恢复,执行流程如下图:

在这里插入图片描述

上图很清晰的分析了 Redis 启动恢复数据的流程,先检查 AOF 文件是否开启,文件是否存在,再检查 RDB 是否开启,文件是否存在。

AOF 常用配置
appendonly no:是否开启 AOF。
appendfilename:AOF 文件名。
dir ./:RDB 文件和 AOF 文件所在目录。
appendfsync everysec:fsync 持久化策略。
no-appendfsync-on-rewrite no:AOF 重写期间是否禁止 fsync。如果开启该选项,可以减轻文件重写时 CPU 和硬盘的负载(尤其是硬盘),但是可能会丢失 AOF 重写期间的数据;需要在负载和安全性之间进行平衡。
auto-aof-rewrite-percentage 100:文件重写触发条件之一。
auto-aof-rewrite-min-size 64mb:文件重写触发提交之一。
aof-load-truncated yes:如果 AOF 文件结尾损坏,Redis 启动时是否仍载入 AOF 文件。
性能问题与解决方案
通过上面的分析,我们都知道 RDB 的快照、AOF 的重写都需要 fork,这是一个重量级操作,会对 Redis 造成阻塞。因此为了不影响 Redis 主进程响应,我们需要尽可能降低阻塞。

优先使用物理机或者高效支持 fork 操作的虚拟化技术。
控制 Redis 实例最大可用内存,fork 耗时跟内存量成正比,线上建议每个 Redis 实例内存控制在 10GB 以内。
合理配置 Linux 内存分配策略,避免物理内存不足导致 fork 失败。
降低 fork 操作的频率,如适度放宽 AOF 自动触发时机,避免不必要的全量复制等。
Redis M/S 是否开启持久化?
极端情况下可以容忍全量数据丢失,那么建议 Master 关闭持久化,Slave 关闭持久化;

极端情况下不能容忍全量数据丢失,但可以容忍部分数据丢失,如果内存数据集较小且不会增长建议 Master 开启 RDB,Slave 开启 RDB;如果数据集很大,或不确定数据集增长趋势,建议 Master 关闭持久化,Slave 开启 RDB。开启 RDB 需要 CPU 和磁盘性能保障。如果 Master 关闭持久化,Slave 开启 RDB 需要保证 Slave 的 RDB 不会被 Master 误重启所覆盖,这里提供几种方案:

重启脚本包一层命令先网络请求加载备机备份目录下的 RDB 文件后再执行 Start,可以防止误重启,但备机调整部署可能需要调整脚本,主机打开持久化也需要调整脚本。
定时将 RDB 文件通过网络 I/O 传给 Master 节点(文件大比较耗时,文件增长需要考虑定时脚本执行间隔,否则会造成持续的网络 I/O),而且也会有一定数据损失。
定时备份 Slave 的 RDB 到备份目录,不做任何其他操作,误重启时人工拷贝 RDB 到 Master 节点(会有一定数据损失)。
最大限度需要数据无损,建议 Master 开启 AOF,Slave 开启 AOF。开启 AOF 需要 CPU 和磁盘性能保障。开启 AOF 建议 fsync 同步刷盘使用 everysec,自定义脚本在应用空闲时定时做 bgrewrite,bgrewrite 期间增量数据做缓冲。

 

redis4以上开启持久化不生效,是因为rdb与aof同时开启,新版redis认为以rdb为主,解决方法:

1.关掉aof持久化配置 appendonly no

2.重启redis后手动启动aof持久化和重写 config appendonly yes   , bgrewriteaof