谈谈 Redis 的持久化---AOF 日志与 RDB 快照

前言

对于 MySQL来说,数据都是持久化在磁盘上的。如果因为误操作删除数据,可以使用 Bin Log 进行恢复,要是突然宕机了,可以借助 Redo Log 进行崩溃恢复,更多的内存可以看我分享的这篇文章

聊聊MySQL里面的undo Log、redo Log和bin Log日志的原子性和持久性已经复制和恢复数据实现过程

而对于 Redis 来说,一般是把数据直接存储在内存中。如果不做任何持久化工作,在出现宕机后,内存中的全部数据就会丢失,很显然是不能容忍这样的情况发生的,这时 Redis 提供了一系列的持久化机制。

Redis 支持两种方式的持久化,一种是 RDB 方式,一种是 AOF 方式。这两种方式可以单独使用其中一种,或者混合使用。

AOF

AOF 是 Append Only File 的缩写,是 Redis 系统提供了一种记录 Redis 操作的持久化方案,Redis 每次执行完一个写类型的语句后,会将该语句以某种格式使用追加的方式顺序写入 AOF 日志,从而达到在 Redis 服务器重启或者当机之后,继续恢复之前数据状态的机制。

AOF 是默认不开启的。

AOF 文件格式

你可以在配置文件中打开 AOF,进入到 redis 安装目录中的redis.conf中:

appendonly yes

这里将 appendonly 的值修改为 yes,即可开启 AOF。

日志文件名:

appendfilename"appendonly.aof

接下来我们打开 redis-cli,操作数据:

在这里插入图片描述

我们打开 appendonly.aof 文件,就可以看到以下内容:
在这里插入图片描述

  • $3 : set 指令的长度。set 长度 == 3
  • set:代表着 set 指令
  • $3:hello key 对应的值长度
  • hello:key 值
  • $3:world value 对应的值长度
  • world:value 值

每当 Redis 执行一个改变数据集的命令时(比如 SET), 这个命令就会被追加到 AOF 文件的末尾。这样的话, 当 Redis 重新启时, 程序就可以通过重新执行 AOF 文件中的命令来达到重建数据集的目的。

当我们首次使用某个客户端执行命令时,客户端会自动帮我们补充 select 0 ,也就是选择 0 的数据库,这个命令也会被保存在 AOF 日志中。

AOF 工作流程:

在这里插入图片描述

AOF 同步策略

  • Redis 收到写命令后首先会追加到 AOF 缓冲区 aof_buf ,而不是直接写入文件系统,因为 AOF 缓冲区是内存提存的,写入速度极高,可以避免每次写入命令到硬盘,导致硬盘 IO 成为 Redis 的负载瓶颈
  • 通过调用系统函数 fsync() 把 AOF 缓冲区的数据真正写到磁盘里面持久化。由于数据是先存储在缓冲区内存里面,如果碰到断电,宕机那么缓冲区里面的数据没来得急落盘就会丢失,因此我们必须有一个相对可靠的机制保证数据落盘

Redis 写命令写入磁盘的命令是通过 appendfsync 来配置的。appendfsync 有三种落盘策略:

  • always: 每次写入一条数据,立即将这个数据对应的写日志 fsync 到磁盘上去,优点是保证数据都不会丢,但是性能非常非常差,吞吐量很低
  • everysec: 每秒将os cache中的数据fsync到磁盘,生产基本就用这个,性能很高,QPS还是可以上万的
  • no: 仅仅 Redis 只负责将数据写入 os cache,后面 os 自己会时不时有自己的策略将数据刷入磁盘,这就不可控了

因为 AOF 的运作方式是不断地将命令追加到文件的末尾,所以随着写入命令的不断增加,随着 AOF 文件越来越大,过大的 AOF 文件不仅会影响服务器的正常运行,也会导致数据恢复需要的时间过长,需要定期对 AOF 文件进行重写,达到压缩的目的,这时候 AOF 重写就派上用场了。

AOF 文件重写 rewrite

为了解决 AOF 文件体积膨胀的问题,Redis 提供了 AOF 文件重写(rewrite)功能。通过该功能,Redis 服务器可以创建一个新的 AOF 文件来替代现有的 AOF 文件,新旧两个 AOF 文件所保存的数据库状态相同,但新 AOF 文件不会包含任何浪费空间的冗余命令,所以新 AOF 文件的体积通常会比旧 AOF 文件的体积要小得多。

AOF 文件重写涉及到两个重要的问题及解决办法如下:

aof_rewrite函数问题:

AOF 重写需要使用到 aof_rewrite 函数,这个函数会进行大量的写入操作,所以调用这个函数的线程将会发生长时间的阻塞,redis是使用单线程来处理命令的,如果直接使用 aof_rewrite 函数,那么 redis 将会在进行 AOF 文件重写时无法处理客户端发送过来的命令请求。

解决这个问题的办法是将重写程序放入子进程中,这样处理的最大好处是:

  • 子进程进行AOF重写期间,主进程可以继续处理命令请求
  • 子进程带有主进程的数据副本,使用子进程而不是线程,可以避免在锁的情况下,保证数据的安全性

子进程进行文件重写存在的问题

子进程在进行 AOF 重写期间,服务器进程还要继续处理命令请求,而新的命令可能对现有的数据进行修改,这会让当前数据库的数据和重写后的 AOF 文件中的数据不一致。

为了解决这种数据不一致的问题,Redis 增加了一个 AOF 重写缓存区,这个缓存在 fork 出子进程之后开始启用,Redis 服务器主进程在执行完写命令之后,会同时将这个写命令追加到 AOF 缓冲区和 AOF 重写缓冲区。

在这里插入图片描述
执行客户端发来的命令请求,将写命令追加到 AOF 区文件中,将写命令追加到 AOF 重写缓存区中。

这么实现的效果是:

  • AOF 缓冲区的内容会定期写入和同步到旧 AOF 文件中,这是为了保证旧 AOF 文件正常工作(因为此时新的 AOF 文件还没写完,所以旧的 AOF 文件需要正常工作)
  • 从创建子进程开始,主进程所处理的所有写操作都会被记录到 AOF 重写缓冲区中

当子进程完成 AOF 文件重写(此时的 AOF 文件中的数据和当前数据库数据状态可能会不一致)之后会向主进程发送一个完成信号,主进程接收到这个信号后将会调用一个函数来完成以下操作:将 AOF 重写缓冲区中的内容全部写入新的 AOF 文件中,此时的 AOF 文件中的数据和当前数据库数据状态一致;对新的 AOF 文件进行改名覆盖原有的旧 AOF 文件,最终完成新旧 AOF 文件的替换。

这个流程如下:

在这里插入图片描述

在整个 AOF 后台重写过程中,只有最后的主进程写入命令到 AOF 缓存和对新的 AOF 文件进行改名,覆盖原有的 AOF 文件。”这两个步骤会造成主进程阻塞,在其他时候,AOF 后台重写都不会对主进程造成阻塞,这将 AOF 重写对性能造成的影响降到最低。

RDB

RDB 的全称是 Redis Database Backup,即数据备份,RDB 方式是通过快照完成的,当符合一定条件时 Redis 会自动将内存中的所有数据进行快照,并且存储到硬盘上。就像拍照一样,将这一瞬间的所有东西都保存下来。

快照文件仅保存数据,不保存额外的操作命令,且经过压缩,因此在恢复速度上快于 AOF。但 RDB 没法做到实时的持久化,因此一般用于数据冷备和复制传输,而 AOF 可以基本做到。

默认 RDB 文件存放路径是当前目录,文件名是:dump.rdb。可以在配置文件中修改路径和文件名,分别是 dir 和dbfilename

在这里插入图片描述

如何让 Redis 生成 RDB 文件

RDB 持久化提供了两种触发策略:一种是手动触发,另一种是自动触发。

通过 SAVE 命令手动触发

SAVE 命令会阻塞 Redis 服务器进程,直到 dump.rdb 文件创建完毕为止,在这个过程中,服务器不能处理任何的命令请求,线上环境不建议使用。

127.0.0.1:6379> save
OK
(1.14s)
59117:M 13 Apr 13:34:51.948 * DB saved on disk

通过 BGSAVE 命令手动触发

客户端可以使用 BGSAVE 命令来创建一个快照,当接收到客户端的 BGSAVE 命令时,Redis 会调用 fork 来创建一个子进程,会先将数据写入到一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件,而父进程则继续处理命令请求,Redis 进程执行 fork 操作创建子进程,RDB 持久化过程由子进程负责,完成后自动结束。阻塞只发生在 fork 阶段,一般时间很短。

127.0.0.1:6379> bgsave
Background saving started
59117:M 13 Apr 13:44:40.312 * Background saving started by pid 59180
59180:C 13 Apr 13:44:40.314 * DB saved on disk
59117:M 13 Apr 13:44:40.317 * Background saving terminated with success

我们看看这个命令怎么实现 RDB 操作的流程图:

在这里插入图片描述
整体的大致流程如下:

  • Redis 客户端执行 BGSAVE 命令或者自动触发 BGSAVE 命令
  • 主进程判断当前是否已经存在正在执行的子进程,如果存在,那么主进程直接返回
  • 如果不存在正在执行的子进程,那么就 fork 一个新的子进程进行持久化数据,fork过程是阻塞的,fork操作完成后
  • 主进程即可执行其他操作
  • 子进程先将数据写入到临时的 RDB 文件中,待快照数据写入完成后再原子替换旧的 RDB 文件
  • 同时发送信号给主进程,通知主进程 RDB 持久化完成,主进程更新相关的统计信

通过配置自动触发

自动触发策略,是指 Redis 在指定的时间内,数据发生了多少次变化时,会自动执行 BGSAVE 命令。自动触发的条件包含在了 Redis 的配置文件 redis.conf 中:

################################ SNAPSHOTTING  ################################
#
# Save the DB on disk:
#
#   save <seconds> <changes>
#
#   Will save the DB if both the given number of seconds and the given
#   number of write operations against the DB occurred.
#
#   In the example below the behaviour will be to save:
#   after 900 sec (15 min) if at least 1 key changed
#   after 300 sec (5 min) if at least 10 keys changed
#   after 60 sec if at least 10000 keys changed
#
#   Note: you can disable saving completely by commenting out all "save" lines.
#
#   It is also possible to remove all the previously configured save
#   points by adding a save directive with a single empty string argument
#   like in the following example:
#
#   save ""

save 900 1
save 300 10
save 60 10000

# By default Redis will stop accepting writes if RDB snapshots are enabled
# (at least one save point) and the latest background save failed.
# This will make the user aware (in a hard way) that data is not persisting
# on disk properly, otherwise chances are that no one will notice and some
# disaster will happen.
#
# If the background saving process will start working again Redis will
# automatically allow writes again.
#
# However if you have setup your proper monitoring of the Redis server
# and persistence, you may want to disable this feature so that Redis will
# continue to work as usual even if there are problems with disk,
# permissions, and so forth.
stop-writes-on-bgsave-error yes

# Compress string objects using LZF when dump .rdb databases?
# For default that's set to 'yes' as it's almost always a win.
# If you want to save some CPU in the saving child set it to 'no' but
# the dataset will likely be bigger if you have compressible values or keys.
rdbcompression yes

# Since version 5 of RDB a CRC64 checksum is placed at the end of the file.
# This makes the format more resistant to corruption but there is a performance
# hit to pay (around 10%) when saving and loading RDB files, so you can disable it
# for maximum performances.
#
# RDB files created with checksum disabled have a checksum of zero that will
# tell the loading code to skip the check.
rdbchecksum yes

# The filename where to dump the DB
dbfilename dump.rdb

# The working directory.
#
# The DB will be written inside this directory, with the filename specified
# above using the 'dbfilename' configuration directive.
#
# The Append Only File will also be created inside this directory.
#
# Note that you must specify a directory here, not a file name.
dir /usr/local/var/db/redis/

  • save 900 1:900 秒内至少出现 1 条写命令就触发
  • save 300 10:300 秒内至少出现 10 条写命令就触发
  • save 60 10000:60 秒内至少出现 10000 条写命令就触发
  • stop-writes-on-bgsave-error:当 stop-writes-on-bgsave-error 设置为 yes 时,表示当备份进程出错时,主进程就会停止接收写入操作,从而保证了持久化数据的一致性
  • rdbcompression:当 rdbcompression 设置为 yes 时,表示在备份的时候,需要将 RDB 文件进行压缩后再进行保存。这里建议将其设置为 no,因为 Redis 本身属于 CPU 密集型服务器,再开启压缩,会带来更多的 CPU 消耗,相比硬盘成本,CPU 性价比更高
  • rdbchecksum:是否对 RDB 文件进程校验
  • dbfilename:配置文件名称,默认 dump.rdb
  • dir:配置rdb文件存放的路劲,这个参数比较重要

只要满足上述 save 三个条件中的任意一个,bgsave 命令就会被执行。

Redis 在进行快照的过程中不会修改 RDB 文件,只有快照结束后才会将旧的文件替换成新的,也就是说任何时候 RDB 文件都是完整的。这使得我们可以通过定时备份 RDB 文件来实现 Redis 数据库备份。RDB 文件是经过压缩的二进制格式,所以占用的空间会小于内存中的数据大小,更加利于传输。

既然 RDB 文件占用小,恢复速度快,那可以大幅增加 RDB 生成的频率吗?

那肯定是不可以的,有可能上一轮 RDB 还未生成,下一轮又开始了。而且也存在性能问题,SAVE 全程都会阻塞主线程,BGSAVE 的 fork 操作同样也会阻塞主线程。

AOF 与 RDB 的区别

在这里插入图片描述

RDB - AOF 混合持久化

Redis 4.0 后持久化新增混合持久化,混合持久化指的是在开启后,将持久化文件同时以 RDB 格式和 AOF 格式 一块写入 aof 文件中,将 aof 重写的数据存入 RDB 中,在生成 RDB 中如果很大,则需要时间,那么这段时间客户端对 Redis 的操作,将会追加写入 aof 文件,并且他们两个最后一块写入 aof 文件中

在这里插入图片描述

RDB-AOF 相关配置:
在这里插入图片描述
当 aof-use-rdb-preamble 设置为 yes 时,表示开启 RDB-AOF 混合持久化模式。

在该模式下,AOF 重写产生的文件将同时包含 RDB 格式的内容和 AOF 格式的内容,该文件的前半段是 RDB 格式的全量数据,而后半段是 Redis 命令格式的增量数据,RDB 做全量持久化,AOF 做增量持久化由于 RDB 是间隔一段时间后才会进行持久化,在此期间内如果 Redis 服务出现问题,则会丢失这一段时间内的数据,因此需要 AOF 来配合使用在 Redis 重启时,会使用 BGSAVE 命令生成 RDB 文件来重新构建内容,再使用 AOF 来重新执行近期的写指令,来实现数据的完整恢复。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值