上一篇文章介绍了 Redis 的基本命令操作,本文主要介绍 Redis 的另一个特性,持久化。
Redis 支持 RDB 和 AOF 两种持久化机制,对内存中的数据进行持久化,可以有效地避免因进程退出造成的数据丢失问题,当下次重启时利用之前持久化的文件即可实现数据恢复
RDB
RDB 持久化把当前进程数据生成快照保存到文件的过程,
优缺点
RDB 持久化方式的优点:
- RDB 是一个紧凑压缩的二进制文件,代表 Redis 在某个时间点上的数据快照。适用于备份, 例如每 24 小时生成 RDB 文件归档,并且每 30 天保存 RDB 文件快照,这样可以方便恢复不同版本的数据;
- RDB 适用于灾难恢复,压缩后的二进制文件便于传递至远程数据中心进行备份恢复;
- RDB 方式采用 fork 子进程的方式进行写入操作,对于父进程不会造成长期阻塞,因此性能较好;
- 加载 RDB 恢复数据远远快于 AOF 的方式。
RDB 持久化方式的缺点:
- RDB 方式数据没办法做到秒级持久化, 不能避免在服务器故障时丢失数据。因为
bgsave
每次运行都要执行fork
操作创建子进程,属于重量级操作,频繁执行成本过高, 可能会至少 5 分钟才保存一次 RDB 文件。在这种情况下,一旦发生故障停机,有可能会丢失分钟的数据。 - 因为每次保存 RDB 时都要
fork
出一个子进程,并由子进程来进行实际的持久化工作。在数据集比较庞大时,fork
可能会比较耗时,造成服务器在短时间内停止处理客户端请求;虽然 AOF 重写也需要进行fork
, 但无论 AOF 重写的执行间隔有多长,数据的耐久性都不会有任何损失。 - RDB 文件使用特定二进制格式保存,Redis 版本演进过程中有多个格式的 RDB 版本,存在老版本 Redis 服务无法兼容新版 RDB 格式的问题。
触发机制
RDB 持久化触发过程可分为手动触发和自动触发。
手动触发 RDB 持久化可通过 save
和 bgsave
命令进行:
save
: 阻塞当前 Redis 服务器,直到 RDB 过程完成为止,如果当前内存较大会造成长时间阻塞,线上环境不建议使用。bgsave
: Redis 进程执行fork
操作创建子进程,RDB 持久化过程由子进程负责,完成后自动结束。阻塞只发生在fork
阶段,一般时间很短。bgsave
命令是针对save
阻塞问题做的优化。因此目前 Redis 内部所有的涉及 RDB 的操作都采用bgsave
的方式。
自动触发 RDB 的持久化机制通常在以下场景:
- 使用
save
相关配置,如save m n
表示m
秒内数据集存在n
次修改时,自动触发bgsave
; - 如果从节点执行全量复制操作,主节点自动执行
bgsave
生成 RDB 文件并发送给从节点; - 执行
debug reload
命令重新加载 Redis 时,也会自动触发save
操作; - 默认情况下执行
shutdown
命令时,如果没有开启 AOF 持久化功能则自动执行bgsave
;
bgsave
流程
对于目前主流的 bgsave
命令,其执行流程如下:
- 执行
bgsave
命令,Redis 父进程判断当前是否存在正在执行的子进程(如 RDB/AOF 子进程),如果存在bgsave
命令直接返回; - 父进程执行
fork
操作创建子进程,fork
操作过程中父进程会阻塞; - 父进程
fork
完成后,bgsave
命令返回Background saving started
信息, 并不再阻塞父进程,可以继续响应其他命令; - 子进程创建 RDB 文件,根据父进程内存生成临时快照文件, 完成后对原有文件进行原子替换;
- 子进程发送信号给父进程表示完成,父进程更新统计信息。
二、AOF
AOF(append only file)持久化:以独立日志的方式记录每次写命令,重启时再重新执行 AOF 文件中的命令达到恢复数据的目的。AOF 的主要作用是解决了数据持久化的实时性,目前已经是 Redis 持久化的主流方式。
优缺点
AOF 持久化方式的优点:
- 使用 AOF 持久化会让 Redis 变得更加耐久,可以设置不同的
fsync
策略。 AOF 的默认策略为每秒钟fsync
一次,在这种配置下,Redis 仍然可以保持良好的性能,并且就算发生故障停机,也最多只会丢失一秒钟的数据。 - AOF 文件是一个只进行追加操作的日志文件,因此对 AOF 文件的写入不需要进行
seek
,即使日志因为某些原因而包含了未写入完整的命令,redis-check-aof 工具也可以轻易地修复这种问题。 - Redis 可以在 AOF 文件体积变得过大时,自动地在后台对 AOF 进行重写,重写后的新 AOF 文件包含了恢复当前数据集所需的最小命令集合。整个重写操作是绝对安全的,因为 Redis 在创建新 AOF 文件的过程中,会继续将命令追加到现有的 AOF 文件里面,即使重写过程中发生停机,现有的 AOF 文件也不会丢失。而一旦新 AOF 文件创建完毕,Redis 就会从旧 AOF 文件切换到新 AOF 文件,并开始对新 AOF 文件进行追加操作。
- AOF 文件有序地保存了对数据库执行的所有写入操作,这些写入操作以 Redis 协议的格式保存,因此 AOF 文件的内容非常容易被人读懂,对文件进行分析(parse)也很轻松。
AOF 持久化方式的缺点:
- 对于相同的数据集,AOF 文件的体积通常要大于 RDB 文件的体积;
- 根据所使用的
fsync
策略,AOF 的速度可能会慢于 RDB。在一般情况下,每秒fsync
的性能依然非常高,而关闭fsync
可以让 AOF 的速度和 RDB 一样快,即使在高负荷之下也是如此。不过在处理巨大的写入载入时,RDB 可以提供更有保证的最大延迟时间; - AOF 在过去曾经发生过这样的 bug :因为个别命令的原因,导致 AOF 文件在重新载入时,无法将数据集恢复成保存时的原样。
使用 AOF
开启 AOF 功能需要设置配置:appendonly yes
,默认不开启。AOF 文件名通过 appendfilename
设置,默认文件名是 appendonly.aof
,保存路径同 RDB 持久化方式一致,通过 dir 配置指定。AOF 工作流程如下:
- 命令写入 (append): 所有的写入命令会追加到 aof_buf (缓冲区) 中, AOF 命令写入的内容直接是文本协议格式。
- 文件同步 (sync): AOF 缓冲区根据对应的策略向硬盘做同步操作;
- 文件重写 (rewrite): 随着 AOF 文件越来越大,需要定期对 AOF 文件进行重写,达到压缩的目的;
- 重启加载 (load): 当 Redis 服务器重启时,可以加载 AOF 文件进行数据恢复。
文件同步:
Redis 提供了三种 AOF 缓冲区同步文件策略,由参数 appendfsync
控制,可配置值为 always
、no
、everysec
:
- 配置为
always
时,命令写入aof_buf
后调用系统fsync
操作同步到 AOF 文件,fsync
完成后线程返回。每次写入都要同步 AOF 文件,在一般的 SATA 硬盘上,Redis 只能支持大约几百 TPS 写入,显然跟 Redis 高性能特性背道而驰,不建议配置。 - 配置为
no
时,命令写入aof_buf
后调用系统write
操作, 不对 AOF 文件做fsync
同步,同步硬盘操作由操作系统负责。由于操作系统每次同步 AOF 文件的周期不可控,而且会加大每次同步硬盘的数据量,虽然提升了性能,但数据安全性无法保证。 - 配置为
everysec
,命令写入aof_buf
后调用系统write
操作,write
完成后线程返回,fsync
同步文件操作由专门线程每秒调用一次。是建议的同步策略,也是默认配置,做到兼顾性能和数据安全性,理论上只有在系统突然宕机的情况下丢失 1 秒的数据。
重写机制:
随着命令不断写入 AOF,文件会越来越大,为了解决这个问题,Redis 引入 AOF 重写机制压缩文件体积。AOF 文件重写是把 Redis 进程内的数据转化为写命令同步到新 AOF 文件的过程。重写后的 AOF 文件变小有如下原因:
- 进程内已经超时的数据不再写入文件。
- 旧的 AOF 文件含有无效命令,重写使用进程内数据直接生成,这样新的 AOF 文件只保留最终数据的写入命令。
- 多条写命令可以合并为一个,为了防止单条命令过大造成客户端缓冲区溢出,对于
list
、set
、hash
、zset
等类型操作,以64
个元素为界拆分为多条。
AOF 重写过程可以手动触发和自动触发:
- 手动触发:直接调用
bgrewriteaof
命令。 - 自动触发:根据
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-min-size &&(aof_current_size-aof_base_size)/ aof_base_size >= auto-aof-rewrite-percentage
AOF 重写流程如下:
- 执行 AOF 重写请求。如果当前进程正在执行 AOF 重写,请求不执行;如果当前进程正在执行
bgsave
操作,重写命令延迟到bgsave
完成之后再执行; - 父进程执行
fork
创建子进程,开销等同于bgsave
过程; - 主进程
fork
操作完成后,继续响应其他命令。所有修改命令依然写入 AOF 缓冲区并根据appendfsync
策略同步到硬盘,保证原有 AOF 机制正确性; - 由于
fork
操作运用写时复制技术,子进程只能共享fork
操作时的内存数据。由于父进程依然响应命令,Redis 使用“AOF 重写缓冲区”保存这部分新数据,防止新 AOF 文件生成期间丢失这部分数据
重启加载:
AOF 和 RDB 文件都可以用于服务器重启时的数据恢复。Redis 持久化文件加载流程如下:
- AOF 持久化开启且存在 AOF 文件时,优先加载 AOF 文件;
- AOF 关闭或者 AOF 文件不存在时,加载 RDB 文件;
- 加载 AOF/RDB 文件成功后,Redis 启动成功;
- AOF/RDB 文件存在错误时,Redis 启动失败并打印错误信息。
RDB 和 AOF 的选择
通常情况下,如果非常关注数据安全性,应该同时使用两种持久化功能。如果可以承受数分钟以内的数据丢失,那么可以只使用 RDB 持久化。并不推荐单独使用 AOF 方式,因为定时生成 RDB 快照(snapshot)非常便于进行数据库备份,并且 RDB 恢复数据集的速度也要比 AOF 恢复的速度要快,除此之外,使用 RDB 还可以避免之前提到的 AOF 程序的 bug 。