Redis 持久化方式
redis 持久化的意义在于故障恢复。如果仅仅把 redis 中的数据存放在内存中,遇到突发的灾难性故障时,redis 宕机,通过重启服务器并恢复进程后,内存中的数据会全部丢失。即便是做了文件持久化,还是会有风险,比如 redis 所在的服务器坏了,或者磁盘烧了,因此业界的解决方案是文件持久化+定期将持久化文件同步至云存储中,即便 redis 所在服务器出现问题,只要再搭建一套 redis 环境,将持久化文件从云端下载到本地,重新导入,即可立刻恢复大部分的数据,重新对外提供服务。(云存储,国外常用亚马逊的S3,国内常用阿里云的ODPS)
一.RDB快照(snapshot)
RDB 持久化方式是通过快照(
snapshotting
)完成的,当符合一定条件时,redis 会自动将内存中所有数据以二进制方式生成一份副本并存储在硬盘上。当 redis 重启时,并且 AOF 持久化未开启时,redis 会读取 RDB 持久化生成的二进制文件(默认名称dump.rdb
,可通过设置dbfilename
修改)进行数据恢复,对于持久化信息可以用过命令info Persistence
查看。
RDB 快照触发条件
- 客户端执行命令
save
和bgsave
会生成快照 - 根据配置文件
save m n
规则进行自动快照 - 主从复制时,从库全量复制同步主库数据,此时主库会执行
bgsave
命令进行快照 - 客户端执行数据库清空命令
flushall
时候,触发快照 - 客户端执行
shutdown
关闭 redis 时,触发快照
1.save 触发方式
该命令会阻塞当前 redis 服务器,执行 save
命令期间,redis 不能处理其他命令,直到 RDB 过程完成为止。
执行完成时候如果存在旧的 RDB 文件,就把新的替代掉旧的。我们的客户端可能都是几万或者是几十万,这种方式显然不可取。
2.bgsave 触发方式
执行该命令时,redis 会在后台异步进行快照操作,快照同时还可以响应客户端请求。
具体操作是 redis 进程执行 fork
操作创建子进程,RDB 持久化过程由子进程负责,完成后自动结束。阻塞只发生在 fork
阶段,一般时间很短。基本上 redis 内部所有的 RDB 操作都是采用 bgsave
命令。
bgsave 命令执行过程
- 客户端执行
bgsave
命令,redis主进程收到指令并判断此时是否在执行bgrewriteaof
( AOF 文件重新过程),如果此时正好在执行则bgsave
直接返回,不fork
子进程,如果没有执行bgrewriteaof
重写 AOF 文件,则进入下一个阶段。 - 主进程调用
fork
方法创建子进程,在创建过程中 redis 主进程阻塞,所以不能响应客户端请求。 - 子进程创建完成以后,
bgsave
命令返回“Background saving started”,此时标志着 redis 可以响应客户端请求了。 - 子进程根据主进程的内存副本创建临时快照文件,当快照文件完成以后对原快照文件进行替换。
- 子进程发送信号给 redis 主进程完成快照操作,主进程更新统计信息(
info Persistence
可查看),子进程退出。
3.save m n规则触发
在指定的 m 秒内,redis 中有 n 个键发生改变,则自动触发 bgsave
。
该规则默认也在 redis.conf
中进行了配置,并且可组合使用,满足其中一个规则,则触发 bgsave
。
redis.conf
里面有如下配置,我们可以去设置:
################################ 快照 #################################
#
# Save the DB on disk:保存数据库到磁盘
#
# save <秒> <更新>
#
# 如果指定的秒数和数据库写操作次数都满足了就将数据库保存。
#
# 下面是保存操作的实例:
# 900秒(15分钟)内至少1个key值改变(则进行数据库保存--持久化)
# 300秒(5分钟)内至少10个key值改变(则进行数据库保存--持久化)
# 60秒(1分钟)内至少10000个key值改变(则进行数据库保存--持久化)
#
# 注释:注释掉“save”这一行配置项就可以让保存数据库功能失效。
#
# 你也可以通过增加一个只有一个空字符串的配置项(如下面的实例)来去掉前面的“save”配置。
#
# save ""
save 900 1 #900秒内如果超过1个key被修改,则发起快照保存
save 300 10 #300秒内容如超过10个key被修改,则发起快照保存
save 60 10000 #60秒内容如超过10000个key被修改,则发起快照保存
#在默认情况下,如果RDB快照持久化操作被激活(至少一个条件被激活)并且持久化操作失败,Redis则会停止接受更新操作。
#这样会让用户了解到数据没有被正确的存储到磁盘上。否则没人会注意到这个问题,可能会造成灾难。
#
#如果后台存储(持久化)操作进程再次工作,Redis会自动允许更新操作。
#
#然而,如果你已经恰当的配置了对Redis服务器的监视和备份,你也许想关掉这项功能。
#如此一来即使后台保存操作出错,redis也仍然可以继续像平常一样工作。
stop-writes-on-bgsave-error yes
#是否在导出.rdb数据库文件的时候采用LZF压缩字符串和对象?
#默认情况下总是设置成‘yes’, 他看起来是一把双刃剑。
#如果你想在存储的子进程中节省一些CPU就设置成'no',
#但是这样如果你的kye/value是可压缩的,你的到处数据接就会很大。
rdbcompression yes
#从版本RDB版本5开始,一个CRC64的校验就被放在了文件末尾。
#这会让格式更加耐攻击,但是当存储或者加载rbd文件的时候会有一个10%左右的性能下降,
#所以,为了达到性能的最大化,你可以关掉这个配置项。
#
#没有校验的RDB文件会有一个0校验位,来告诉加载代码跳过校验检查。
rdbchecksum yes
# 导出数据库的文件名称
dbfilename dump.rdb
# 工作目录
# 注意你一定要在这个配置一个工作目录,而不是文件名称。
dir ./
4.主从触发
在 redis 主从复制中,从节点执行全量复制操作,主节点会执行 bgsave
命令,并将 RDB 文件发送给从节点。
5.flushall 触发
flushall
命令用于清空数据库,请慎用,当我们使用了则表明我们需要对数据进行清空,那 redis 当然需要对快照文件也进行清空,所以会触发 bgsave
。
6.shutdown 触发
shutdown
命令触发就不用说了, redis 在关闭前处于安全角度将所有数据全部保存下来,以便下次启动会恢复。
RDB 持久化方式优缺点
优点:
- RDB 机制会生成多份数据文件,分别代表着各个时刻中 redis 的数据,这种多个数据的备份方式非常适合做冷备份。在实际应用中,我们可以在 Linux 服务器上部署 shell 脚本,添加定时任务,定时扫描并将 RDB 文件上传到远程安全的云存储上。
- 使用 RDB 机制做备份,对于 redis 对外提供服务的影响非常小,可以让 redis 保持高性能。redis主进程只需要
fork
一个子进程,让子进程执行磁盘的 IO 操作,完成 RDB 的持久化。 - 相对于 AOF 持久化机制来说,使用 RDB 数据文件来重启和恢复 redis 进程的速度更快。因为 AOF 中存放的是指令日志,数据恢复时需要回放和执行所有的指令日志,而 RDB 本身就是一份数据文件,直接加载到内存即可。
缺点:
- 由于执行 RDB 生成数据快照的时间间隔基本在分钟级别,甚至更长,一旦服务器宕机,那么就会永久性的丢失上一次 RDB 文件生成到宕机的这段时间内的数据。正因为如此,RDB 不适合作为最优先选择的数据备份方案。
- 虽然 RDB 文件持久化的工作不需要主进程执行,但遇到内存中数据量非常大的场景下,主进程
fork
子进程花费的时间较多(可能达到数秒),此期间 redis 会暂停对外提供服务。
二.AOF 机制
AOF 机制将每一条写入命令作为日志,以
append-only
的模式写入磁盘中的一份日志文件中。AOF 文件内存放的是一条条的写命令(如 set,del,add 等),文本类型,按照 redis 的命令请求协议选择合适的格式来保存文件。
为了提高写入效率,在现代操作系统内执行写入操作时,先将数据暂时写入到内存中(OS Cache),等到缓冲区的空间被填满或间隔一定时间后,才真正地将缓冲区内的数据写入到本次磁盘上。但这种做法存在一个安全问题,如果遇到突发性的灾难故障,那么尚在缓冲区内的数据将永久性的丢失。为此操作系统提供了 fsync
和 fdatasync
两个函数,它们可以强制让操作系统立刻将缓冲区内的全部数据刷新(写入)到本地磁盘中。
AOF 持久化机制默认是没有开启的,需要在 redis.conf
配置文件中配置开启。
############################## APPEND ONLY MODE ###############################
# 默认情况下,Redis将数据集异步转储到磁盘上。这种模式是
# 在许多应用程序中已经足够好了,但Redis进程或
# 断电可能导致几分钟的写操作丢失(取决于具体情况)
# 配置的存储点)。
#
# 仅追加文件是一种提供
# 耐久性更好。例如,使用默认数据fsync策略
# (请参阅配置文件的后面部分)Redis在一个数据库中只会丢失一秒钟的写操作
# 戏剧性的事件,如服务器断电,或在发生故障时进行一次写入
# Redis进程本身出现错误,但操作系统是错误的
# 仍然正常运行。
#
# AOF和RDB持久性可以同时启用而不会出现问题。
# 如果启动时启用了AOF,Redis将加载AOF,即文件
# 具有更好的耐久性保证。
appendonly no # aof 持久化开关
# 仅追加文件的名称(默认值:“appendonly.aof”)
appendfilename "appendonly.aof" # aof 文件名称
# Redis支持三种不同的模式:
# appendfsync always # 每个redis写命令都要同步写入硬盘,严重降低redis速度
appendfsync everysec # 每秒执行一次同步显式的将多个写命令同步到磁盘
# appendfsync no # 由操作系统决定何时同步
# 当AOF fsync策略设置为always或everysec,并且后台保存进程(后台保存或AOF日志后台重写)对磁盘执行大量I / O时,在某些Linux配置中,Redis可能会在磁盘上阻塞太长时间。
# fsync()调用。请注意,目前尚无此修复程序,因为即使在其他线程中执行fsync也将阻止我们的同步write(2)调用。
# 为了缓解此问题,可以使用以下选项来防止在BGSAVE或BGREWRITEAOF进行时在主进程中调用fsync()。
# 这意味着当另一个进程正在保存时,Redis的持久性与“ appendfsync none”相同。
# 实际上,这意味着在最坏的情况下(使用默认的Linux设置),最多可能会丢失30秒的日志。
# 如果存在延迟问题,请将其设置为“是”。否则,从耐用性的角度来看,这是最安全的选择.
no-appendfsync-on-rewrite no
# 为防止 AOF 文件过大,redis 会重写 aof 文件,这里设置的就是触发 AOF 重写的百分比和大小阈值。百分比置为0即禁止重写
auto-aof-rewrite-percentage 100 # 自动改写百分比100
auto-aof-rewrite-min-size 64mb # 自动改写最小大小64mb
# 当AOF数据重新加载到内存中时,在Redis启动过程中可能会发现AOF文件在末尾被截断。
# 当运行Redis的系统崩溃时,尤其是在没有data = ordered选项的情况下挂载ext4文件系统时,可能会发生这种 情况(但是,当Redis本身崩溃或中止,但操作系统仍然可以正常运行时,就不会发生这种情况)。
# Redis可以在发生这种情况时退出并显示错误,也可以加载尽可能多的数据(当前为默认值),如果发现AOF文件最后被截断,则可以开始。以下选项控制此行为。
# 如果aof-load-truncated设置为yes,则将加载截断的AOF文件,并且Redis服务器将开始发出日志以将事件通知用户。否则,如果该选项设置为no,则服务器将中止并显示错误,并拒绝启动。
# 如果将该选项设置为no,则用户需要在重新启动服务器之前使用“ redis-check-aof”实用程序修复AOF文件。
# 请注意,如果在中间发现AOF文件已损坏,则服务器仍将退出并出现错误。
# 仅当Redis尝试从AOF文件读取更多数据但找不到足够的字节时,此选项才适用.
aof-load-truncated yes
AOF rewrite原理
一个 redis 节点中有且只会有一份 AOF 文件,随着 redis 运行时间变长,AOF 内记录的写请求信息会越来越多,当文件的大小接近阈值时,redis 会基于内存中现有的数据,对 AOF 文件执行
rewrite
操作,redis 会创建一个新的 AOF 文件来替代现有的 AOF 文件,并删除旧文件,新旧两个文件所保存的数据库状态是相同的,但是新的 AOF 文件不会包含任何浪费空间的冗余命令,通常体积会较旧 AOF 文件小很多(注意: 由于内存大小有限,内存内的数据量膨胀到一定级别后,会自动执行缓存淘汰算法,比如LRU).
AOF rewrite 过程
- redis
fork
一个子进程。 - 子进程基于当前内存中的数据,开始往一个新的临时的 AOF 文件 中写入日志。
- redis 主进程接收到客户端新的写操作之后,会将新的日志写入内存,同时写入到旧的 AOF 文件。
- 子进程写完新的日志文件之后,redis 主进程将内存中的新日志再次追加到新的 AOF 文件中。
- 用新的日志文件替换掉旧的日志文件。
AOF 持久化方式优缺点
优点:
- AOF 机制可以做到每隔1秒备份一次数据(将数据写入OS Cache),接着通过后台进程执行
fsync
操作,最多只丢失1秒钟的数据,更好的保护的数据的完整性。 - AOF 日志 文件以
append-only
模式写入,所以没有任何的磁盘寻址开销,写入的性能非常高,而且文件不容易破损,即使文件尾部破损,也很容易修复(redis-check-aof --fix
)。
缺点:
- 对于同一份数据来说,AOF 日志文件通常比 RDB 的快照文件大。
- AOF 开启后,支持的写 QPS 会比开启 RDB 支持的写 QPS 低,这是因为 AOF 在进行
fsync
时,会在内存和磁盘之间产品 IO 操作,如果fsync
过于频繁,比如为了确保数据完全不丢失,每写入一条指令都执行一次fsync
,那么就会极大的降低 redis 对外提供的写性能。实际生产环境证明,保持在1秒左右的fsync
执行频率不会对 redis 的写性能造成太大影响。 - 做数据恢复的时候比较慢,做冷备(比如定期备份)不太方便,需要自己手写比较复杂的脚本。
三.RDB 和 AOF 同时使用进行数据备份
如果同时使用 RDB 和 AOF 进行持久化,在数据恢复阶段将优先使用 AOF 文件,这是因为 AOF 一般间隔几秒钟写入一次请求,而 RDB 一般间隔几分钟才会备份一次内存中数据的镜像,因此从数据恢复的角度来看,通过 AOF 恢复数据后,丢失的数据少于 RDB。如果 AOF 关闭状态时,则从 RDB 中恢复。
四.混合持久化
看了上面的 RDB 和 AOF 的介绍后,我们可以发现,使用 RDB 持久化会有数据丢失的风险,但是恢复速度快,而使用 AOF 持久化可以保证数据完整性,但恢复数据的时候会很慢。于是从 redis4 之后新增了混合 AOF 和 RDB 的模式,先使用 RDB 进行快照存储,然后使用 AOF 持久化记录所有的写操作,当重写策略满足或手动触发重写的时候,将最新的数据存储为新的 RDB 记录。这样的话,重启服务的时候会从 RDB 和 AOF 两部分恢复数据,即保证了数据完整性,又提高了恢复的性能。
aof-rdb 持久化机制默认是没有开启的,需要在 redis.conf
配置文件中配置开启,并开启 AOF 持久化。
# 重写AOF文件时,Redis能够在
# AOF文件用于更快的重写和恢复。启用此选项时
# 在重写的AOF上,文件由两个不同的节组成:
#
# [RDB文件][AOF tail]
#
# 加载Redis时,会识别AOF文件以“Redis”开头
# 字符串并加载带前缀的RDB文件,然后继续加载AOF
# 尾巴。
#
# 默认情况下,此选项当前处于关闭状态,以避免意外情况
# 格式更改,但在某个时候将用作默认值.
# 混合持久化开关
aof-use-rdb-preamble yes
混合持久化开启,系统根据策略触发 AOF rewrite
时,fork
一个子线程将内存数据以 RDB 二进制格式写入 AOF 文件头部,那些在重写操作执行之后执行的 redis 命令,则以 AOF 持久化的方式追加到 AOF 文件的末尾。
触发条件:
因为混合持久化是基于 AOF 重写的,所以和 AOF rewrite
的机制一致 。
开启混合存储模式后 aof 文件加载的流程如下
- AOF 文件开头是 RDB 的格式, 先加载 RDB 内容再加载剩余的 AOF。
- AOF 文件开头不是 RDB 的格式,直接以 AOF 格式加载整个文件。