Redis 的持久化方案

*爱你&永不变心* 提交于 2020-05-03 19:58:27

目录

RDB

RDB 持久化是把当前进程数据生成快照保存到硬盘的过程。 触发 RDB 持久化过程分为手动触发和自动触发。RDB 完成后会自动生成一个文件,保存在 dir 配置的指定目录下,文件名是 dbfileName 指定。Redis 默认会采用 LZF 算法对生成的 RDB 文件做压缩处理,压缩后的文件远远小于内存大小,默认开启。

手动触发:命令有 save 和 bgsave

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

自动触发

  • 根据我们的 save m n 配置规则自动触发。
  • 如果从节点执行全量复制操作, 主节点自动执行 bgsave 生成 RDB 文件并发送给从节点。
  • 执行 debug reload 命令重新加载 Redis 时, 也会自动触发 save 操作。
  • 默认情况下执行 shutdown 命令时, 如果没有开启 AOF 持久化功能则自动执行 bgsave。

RDB 执行流程:
在这里插入图片描述

  1. 执行 bgsave 命令后,会先判断是否存在 AOF 或者 RDB 的子进程,如果存在,直接返回。
  2. 父进程 fork 操作创建一个子进程,fork 操作中父进程会被阻塞。
  3. fork 完成后,子进程开始根据父进程的内存生成临时快照文件,完成后对原有的 RDB 文件进行替换。执行 lastsave 命令可以查看最近一次的 RDB 时间。
  4. 子进程完成后发送信号给父进程,父进程更新统计信息。

RDB 的优点:

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

RDB 的缺点:

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

AOF

AOF(append only file)持久化,以独立日志的方式记录每次写命令,重启时再重新执行 AOF 文件中的命令达到恢复数据的目的。AOF 的主要作用是解决了数据持久化的实时性, 目前已经是 Redis 持久化的主流方式。

开启 AOF 功能需要设置配置 appendonly yes, 默认不开启。AOF 文件名通过 appendfilename 配置设置, 默认文件名是 appendonly.aof。保存路径同 RDB 持久化方式一致,通过 dir 配置指定。

AOF 整体的执行流程:大致分为命令写入、文件同步、文件重写、重启加载四个步骤
在这里插入图片描述

命令写入

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

*3\r\n$3\r\nset\r\n$5\r\nhello\r\n$5\r\nworld\r\n

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

文件同步

Redis 提供了多种 AOF 缓冲区同步文件策略, 由参数 appendfsync 控制,如下:

  • 配置为 always 时, 每次写入都要同步 AOF 文件, 在一般的 SATA 硬盘上,Redis 只能支持大约几百 TPS 写入, 显然跟 Redis 高性能特性背道而驰,不建议配置。
  • 配置为 no,由于操作系统每次同步 AOF 文件的周期不可控,而且会加大每次同步硬盘的数据量,虽然提升了性能,但数据安全性无法保证。
  • 配置为 everysec(默认),是建议的同步策略, 也是默认配置,做到兼顾性能和数据安全性。理论上只有在系统突然宕机的情况下丢失 1 秒的数据(当然,这是不太准确的)。

文件重写

为什么要文件重写呢? 因为文件重写能够使得 AOF 文件的体积变得更小,从而使得可以更快的被 Redis 加载。

随着命令不断写入 AOF, 文件会越来越大, 为了解决这个问题, Redis 引入 AOF 重写机制压缩文件体积。AOF 文件重写是把 Redis 进程内的数据转化为写命令同步到新 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-minsize && (aof_current_size - aof_base_size) / aof_base_size >= auto-aof-rewritepercentage

其中,aof_current_size 和 aof_base_size 可以在 info Persistence 统计信息中查看到。

那么文件重写后的 AOF 文件为什么会变小呢? 有如下几个原因:

  • 进程内已经超时的数据将不会再次写入 AOF 文件中。
  • 旧的 AOF 文件含有无效命令,如 del key1、 hdel key2 等。重写使用进程内数据直接生成,这样新的 AOF 文件只保留最终数据的写入命令。
  • 多条写命令可以合并为一个,如:lpush list a、 lpush list b、lpush listc 可以转化为:lpush list a b c。为了防止单条命令过大造成客户端缓冲区溢出,对于 list、set、hash、zset 等类型操作,以 64 个元素为界拆分为多条。

介绍了文件重写的系列知识,下面来看看 Redis 内部是如何进行文件重写的,如下图:
在这里插入图片描述

  1. 重写期间,主线程并没有阻塞,而是在执行其他的操作命令,依然会向旧的 AOF 文件写入数据,这样能够保证备份的最终完整性,如果数据重写失败,也能保证数据不会丢失。
  2. 为了把重写期间响应的写入信息也写入到新的文件中,因此也会为子进程保留一个缓冲区,防止新写的文件丢失数据。
  3. 重写是直接把当前内存的数据生成对应命令,并不需要读取老的 AOF 文件进行分析、命令合并。
  4. AOF 文件直接采用的文本协议,主要是兼容性好、追加方便、可读性高可认为修改修复。
  5. 无论是 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 是否开启,文件是否存在。

性能问题与解决方案

通过上面的分析,我们都知道 RDB 的快照、AOF 的重写都需要 fork,这是一个重量级操作,会对 Redis 造成阻塞。因此为了不影响 Redis 主进程响应,我们需要尽可能降低阻塞。

  1. 优先使用物理机或者高效支持 fork 操作的虚拟化技术。
  2. 控制 Redis 实例最大可用内存,fork 耗时跟内存量成正比,线上建议每个 Redis 实例内存控制在 10GB 以内。
  3. 合理配置 Linux 内存分配策略,避免物理内存不足导致 fork 失败。
  4. 降低 fork 操作的频率,如适度放宽 AOF 自动触发时机,避免不必要的全量复制等。
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!