1 Star 0 Fork 169

章志成 / db-tutorial

forked from turnon / db-tutorial 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
redis-persistence.md 18.67 KB
一键复制 编辑 原始数据 按行查看 历史
turnon 提交于 2021-05-13 16:57 . docs: 文档整理

Redis 持久化

Redis 支持持久化,即把数据存储到硬盘中。

Redis 提供了两种持久化方式:

  • RDB 快照(snapshot) - 将存在于某一时刻的所有数据都写入到硬盘中。
  • 只追加文件(append-only file,AOF) - 它会在执行写命令时,将被执行的写命令复制到硬盘中。

这两种持久化方式既可以同时使用,也可以单独使用。

将内存中的数据存储到硬盘的一个主要原因是为了在之后重用数据,或者是为了防止系统故障而将数据备份到一个远程位置。另外,存储在 Redis 里面的数据有可能是经过长时间计算得出的,或者有程序正在使用 Redis 存储的数据进行计算,所以用户会希望自己可以将这些数据存储起来以便之后使用,这样就不必重新计算了。

Redis 提供了两种持久方式:RDB 和 AOF。你可以同时开启两种持久化方式。在这种情况下, 当 redis 重启的时候会优先载入 AOF 文件来恢复原始的数据,因为在通常情况下 AOF 文件保存的数据集要比 RDB 文件保存的数据集要完整。

一、RDB

RDB 简介

RDB 即快照方式,它将某个时间点的所有 Redis 数据保存到一个经过压缩的二进制文件(RDB 文件)中

创建 RDB 后,用户可以对 RDB 进行备份,可以将 RDB 复制到其他服务器从而创建具有相同数据的服务器副本,还可以在重启服务器时使用。一句话来说:RDB 适合作为 冷备

RDB 既可以手动执行,也可以根据服务器配置选项定期执行。该功能可以将某个时间点的数据库状态保存到一个 RDB 文件中。

RDB 的优点

  • RDB 文件非常紧凑,适合作为冷备。比如你可以在每个小时报保存一下过去 24 小时内的数据,同时每天保存过去 30 天的数据,这样即使出了问题你也可以根据需求恢复到不同版本的数据集。
  • 快照在保存 RDB 文件时父进程唯一需要做的就是 fork 出一个子进程,接下来的工作全部由子进程来做,父进程不需要再做其他 IO 操作,所以快照持久化方式可以最大化 Redis 的性能。
  • 恢复大数据集时,RDB 比 AOF 更快

RDB 的缺点

  • 如果系统发生故障,将会丢失最后一次创建快照之后的数据。如果你希望在 Redis 意外停止工作(例如电源中断)的情况下丢失的数据最少的话,那么 快照不适合你。虽然你可以配置不同的 save 时间点(例如每隔 5 分钟并且对数据集有 100 个写的操作),是 Redis 要完整的保存整个数据集是一个比较繁重的工作,你通常会每隔 5 分钟或者更久做一次完整的保存,万一在 Redis 意外宕机,你可能会丢失几分钟的数据。
  • 如果数据量很大,保存快照的时间会很长。快照需要经常 fork 子进程来保存数据集到硬盘上。当数据集比较大的时候,fork 的过程是非常耗时的,可能会导致 Redis 在一些毫秒级内不能响应客户端的请求。如果数据集巨大并且 CPU 性能不是很好的情况下,这种情况会持续 1 秒。AOF 也需要 fork,但是你可以调节重写日志文件的频率来提高数据集的耐久度。

RDB 的创建

有两个 Redis 命令可以用于生成 RDB 文件:SAVEBGSAVE

  • SAVE 命令会阻塞 Redis 服务器进程,直到 RDB 创建完成为止,在阻塞期间,服务器不能响应任何命令请求。
  • BGSAVE 命令会派生出(fork)一个子进程,然后由子进程负责创建 RDB 文件,服务器进程(父进程)继续处理命令请求。

:bell: 注意:BGSAVE 命令执行期间,SAVEBGSAVEBGREWRITEAOF 三个命令会被拒绝,以免与当前的 BGSAVE 操作产生竞态条件,降低性能。

自动间隔保存

Redis 允许用户通过设置服务器配置的 save 选项,让服务器每隔一段时间自动执行一次 BGSAVE 命令。

用户可以通过 save 选项设置多个保存条件,但只要其中任意一个条件被满足,服务器就会执行 BGSAVE 命令。

举例来说,redis.conf 中设置了如下配置:

save 900 1       -- 900 秒内,至少对数据库进行了 1 次修改
save 300 10      -- 300 秒内,至少对数据库进行了 10 次修改
save 60 10000    -- 60 秒内,至少对数据库进行了 10000 次修改

只要满足以上任意条件,Redis 服务就会执行 BGSAVE 命令。

RDB 的载入

RDB 文件的载入工作是在服务器启动时自动执行的,Redis 并没有专门用于载入 RDB 文件的命令。

服务器载入 RDB 文件期间,会一直处于阻塞状态,直到载入完成为止。

🔔 注意:因为 AOF 通常更新频率比 RDB 高,所以丢失数据相对更少。基于这个原因,Redis 有以下默认行为:

  • 只有在关闭 AOF 功能的情况下,才会使用 RDB 还原数据,否则优先使用 AOF 文件来还原数据。

RDB 的文件结构

RDB 文件是一个经过压缩的二进制文件,由多个部分组成。

对于不同类型(STRING、HASH、LIST、SET、SORTED SET)的键值对,RDB 文件会使用不同的方式来保存它们。

img

Redis 本身提供了一个 RDB 文件检查工具 redis-check-dump。

RDB 的配置

Redis RDB 默认配置如下:

save 900 1
save 300 10
save 60 10000
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb
dir ./

Redis 的配置文件 redis.conf 中与 RDB 有关的选项:

  • save - Redis 会根据 save 选项,让服务器每隔一段时间自动执行一次 BGSAVE 命令。

  • stop-writes-on-bgsave-error - 当 BGSAVE 命令出现错误时停止写 RDB 文件

  • rdbcompression - RDB 文件开启压缩功能。

  • rdbchecksum - 对 RDB 文件进行校验。

  • dbfilename - RDB 文件名。

  • dir - RDB 文件和 AOF 文件的存储路径。

二、AOF

AOF 简介

AOF(Append Only File) 是以 文本日志形式所有写命令以 Redis 命令请求协议格式追加到 AOF 文件的末尾,以此来记录数据的变化。当服务器重启时,会重新载入和执行 AOF 文件中的命令,就可以恢复原始的数据。AOF 适合作为 热备

AOF 可以通过 appendonly yes 配置选项来开启。

命令请求会先保存到 AOF 缓冲区中,之后再定期写入并同步到 AOF 文件。

AOF 的优点

  • 如果系统发生故障,AOF 丢失数据比 RDB 少。你可以使用不同的 fsync 策略:无 fsync;每秒 fsync;每次写的时候 fsync。使用默认的每秒 fsync 策略,Redis 的性能依然很好(fsync 是由后台线程进行处理的,主线程会尽力处理客户端请求),一旦出现故障,你最多丢失 1 秒的数据。
  • AOF 文件可修复 - AOF 文件是一个只进行追加的日志文件,所以不需要写入 seek,即使由于某些原因(磁盘空间已满,写的过程中宕机等等)未执行完整的写入命令,你也也可使用 redis-check-aof 工具修复这些问题。
  • AOF 文件可压缩。Redis 可以在 AOF 文件体积变得过大时,自动地在后台对 AOF 进行重写:重写后的新 AOF 文件包含了恢复当前数据集所需的最小命令集合。整个重写操作是绝对安全的,因为 Redis 在创建新 AOF 文件的过程中,会继续将命令追加到现有的 AOF 文件里面,即使重写过程中发生停机,现有的 AOF 文件也不会丢失。而一旦新 AOF 文件创建完毕,Redis 就会从旧 AOF 文件切换到新 AOF 文件,并开始对新 AOF 文件进行追加操作。
  • AOF 文件可读 - AOF 文件有序地保存了对数据库执行的所有写入操作,这些写入操作以 Redis 命令的格式保存。因此 AOF 文件的内容非常容易被人读懂,对文件进行分析(parse)也很轻松。 导出(export) AOF 文件也非常简单。举个例子,如果你不小心执行了 FLUSHALL 命令,但只要 AOF 文件未被重写,那么只要停止服务器,移除 AOF 文件末尾的 FLUSHALL 命令,并重启 Redis ,就可以将数据集恢复到 FLUSHALL 执行之前的状态。

AOF 的缺点

  • AOF 文件体积一般比 RDB 大 - 对于相同的数据集来说,AOF 文件的体积通常要大于 RDB 文件的体积。
  • 恢复大数据集时,AOF 比 RDB 慢。 - 根据所使用的 fsync 策略,AOF 的速度可能会慢于快照。在一般情况下,每秒 fsync 的性能依然非常高,而关闭 fsync 可以让 AOF 的速度和快照一样快,即使在高负荷之下也是如此。不过在处理巨大的写入载入时,快照可以提供更有保证的最大延迟时间(latency)。

AOF 的创建

Redis 命令请求会先保存到 AOF 缓冲区,再定期写入并同步到 AOF 文件

AOF 的实现可以分为命令追加(append)、文件写入、文件同步(sync)三个步骤。

  • 命令追加 - 当 Redis 服务器开启 AOF 功能时,服务器在执行完一个写命令后,会以 Redis 命令协议格式将被执行的写命令追加到 AOF 缓冲区的末尾。
  • 文件写入文件同步 - Redis 的服务器进程就是一个事件循环,这个循环中的文件事件负责接收客户端的命令请求,以及向客户端发送命令回复。而时间事件则负责执行定时运行的函数。因为服务器在处理文件事件时可能会执行写命令,这些写命令会被追加到 AOF 缓冲区,服务器每次结束事件循环前,都会根据 appendfsync 选项来判断 AOF 缓冲区内容是否需要写入和同步到 AOF 文件中。

appendfsync 不同选项决定了不同的持久化行为:

  • always - 将缓冲区所有内容写入并同步到 AOF 文件。
  • everysec - 将缓冲区所有内容写入到 AOF 文件,如果上次同步 AOF 文件的时间距离现在超过一秒钟,那么再次对 AOF 文件进行同步,这个同步操作是有一个线程专门负责执行的。
  • no - 将缓冲区所有内容写入到 AOF 文件,但并不对 AOF 文件进行同步,何时同步由操作系统决定。

AOF 的载入

因为 AOF 文件中包含了重建数据库所需的所有写命令,所以服务器只要载入并执行一遍 AOF 文件中保存的写命令,就可以还原服务器关闭前的数据库状态。

AOF 载入过程如下:

  1. 服务器启动载入程序。
  2. 创建一个伪客户端。因为 Redis 命令只能在客户端上下文中执行,所以需要创建一个伪客户端来载入、执行 AOF 文件中记录的命令。
  3. 从 AOF 文件中分析并读取一条写命令。
  4. 使用伪客户端执行写命令。
  5. 循环执行步骤 3、4,直到所有写命令都被处理完毕为止。
  6. 载入完毕。

AOF 的重写

随着 Redis 不断运行,AOF 的体积也会不断增长,这将导致两个问题:

  • AOF 耗尽磁盘可用空间。
  • Redis 重启后需要执行 AOF 文件记录的所有写命令来还原数据集,如果 AOF 过大,则还原操作执行的时间就会非常长。

为了解决 AOF 体积膨胀问题,Redis 提供了 AOF 重写功能,来对 AOF 文件进行压缩。AOF 重写可以产生一个新的 AOF 文件,这个新的 AOF 文件和原来的 AOF 文件所保存的数据库状态一致,但体积更小

AOF 重写并非读取和分析现有 AOF 文件的内容,而是直接从数据库中读取当前的数据库状态。即依次读取数据库中的每个键值对,然后用一条命令去记录该键值对,以此代替之前可能存在冗余的命令。

AOF 后台重写

作为一种辅助性功能,显然 Redis 并不想在 AOF 重写时阻塞 Redis 服务接收其他命令。因此,Redis 决定通过 BGREWRITEAOF 命令创建一个子进程,然后由子进程负责对 AOF 文件进行重写,这与 BGSAVE 原理类似。

  • 在执行 BGREWRITEAOF 命令时,Redis 服务器会维护一个 AOF 重写缓冲区。当 AOF 重写子进程开始工作后,Redis 每执行完一个写命令,会同时将这个命令发送给 AOF 缓冲区和 AOF 重写缓冲区。
  • 由于彼此不是在同一个进程中工作,AOF 重写不影响 AOF 写入和同步。当子进程完成创建新 AOF 文件的工作之后,服务器会将重写缓冲区中的所有内容追加到新 AOF 文件的末尾,使得新旧两个 AOF 文件所保存的数据库状态一致。
  • 最后,服务器用新的 AOF 文件替换就的 AOF 文件,以此来完成 AOF 重写操作。

img

可以通过设置 auto-aof-rewrite-percentageauto-aof-rewrite-min-size,使得 Redis 在满足条件时,自动执行 BGREWRITEAOF

假设配置如下:

auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

表明,当 AOF 大于 64MB,且 AOF 体积比上一次重写后的体积大了至少 100% 时,执行 BGREWRITEAOF

AOF 的配置

AOF 的默认配置:

appendonly no
appendfsync everysec
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

AOF 持久化通过在 redis.conf 中的 appendonly yes 配置选项来开启。

  • appendonly - 开启 AOF 功能。
  • appendfilename - AOF 文件名。
  • appendfsync - 用于设置同步频率,它有以下可选项:
    • always - 每个 Redis 写命令都要同步写入硬盘。这样做会严重降低 Redis 的速度。
    • everysec - 每秒执行一次同步,显示地将多个写命令同步到硬盘。为了兼顾数据安全和写入性能,推荐使用 appendfsync everysec 选项。Redis 每秒同步一次 AOF 文件时的性能和不使用任何持久化特性时的性能相差无几。
    • no - 让操作系统来决定应该何时进行同步。
  • no-appendfsync-on-rewrite - AOF 重写时不支持追加命令。
  • auto-aof-rewrite-percentage - AOF 重写百分比。
  • auto-aof-rewrite-min-size - AOF 重写文件的最小大小。
  • dir - RDB 文件和 AOF 文件的存储路径。

三、RDB 和 AOF

当 Redis 启动时, 如果 RDB 和 AOF 功能都开启了,那么程序会优先使用 AOF 文件来恢复数据集,因为 AOF 文件所保存的数据通常是最完整的。

如何选择持久化

  • 如果不关心数据丢失,可以不持久化。
  • 如果可以承受数分钟以内的数据丢失,可以只使用 RDB。
  • 如果不能承受数分钟以内的数据丢失,可以同时使用 RDB 和 AOF。

有很多用户都只使用 AOF 持久化, 但并不推荐这种方式: 因为定时生成 RDB 快照(snapshot)非常便于进行数据库备份,并且快照恢复数据集的速度也要比 AOF 恢复的速度要快,除此之外,使用快照还可以避免之前提到的 AOF 程序的 bug 。

RDB 切换为 AOF

在 Redis 2.2 或以上版本,可以在不重启的情况下,从 RDB 切换为 AOF :

  • 为最新的 dump.rdb 文件创建一个备份。
  • 将备份放到一个安全的地方。
  • 执行以下两条命令:
  • redis-cli config set appendonly yes
  • redis-cli config set save
  • 确保写命令会被正确地追加到 AOF 文件的末尾。
  • 执行的第一条命令开启了 AOF 功能: Redis 会阻塞直到初始 AOF 文件创建完成为止, 之后 Redis 会继续处理命令请求, 并开始将写入命令追加到 AOF 文件末尾。

执行的第二条命令用于关闭快照功能。 这一步是可选的, 如果你愿意的话, 也可以同时使用快照和 AOF 这两种持久化功能。

:bell: 重要:别忘了在 redis.conf 中打开 AOF 功能!否则的话,服务器重启之后,之前通过 CONFIG SET 设置的配置就会被遗忘,程序会按原来的配置来启动服务器。

AOF 和 RDB 的相互作用

BGSAVEBGREWRITEAOF 命令不可以同时执行。这是为了避免两个 Redis 后台进程同时对磁盘进行大量的 I/O 操作。

如果 BGSAVE 正在执行,并且用户显示地调用 BGREWRITEAOF 命令,那么服务器将向用户回复一个 OK 状态,并告知用户,BGREWRITEAOF 已经被预定执行。一旦 BGSAVE 执行完毕, BGREWRITEAOF 就会正式开始。

四、Redis 备份

应该确保 Redis 数据有完整的备份。

备份 Redis 数据建议采用 RDB。

备份过程

  1. 创建一个定期任务(cron job),每小时将一个 RDB 文件备份到一个文件夹,并且每天将一个 RDB 文件备份到另一个文件夹。
  2. 确保快照的备份都带有相应的日期和时间信息,每次执行定期任务脚本时,使用 find 命令来删除过期的快照:比如说,你可以保留最近 48 小时内的每小时快照,还可以保留最近一两个月的每日快照。
  3. 至少每天一次,将 RDB 备份到你的数据中心之外,或者至少是备份到你运行 Redis 服务器的物理机器之外。

容灾备份

Redis 的容灾备份基本上就是对数据进行备份,并将这些备份传送到多个不同的外部数据中心。

容灾备份可以在 Redis 运行并产生快照的主数据中心发生严重的问题时,仍然让数据处于安全状态。

五、要点总结

img

参考资料

Python
1
https://gitee.com/gump12138/db-tutorial.git
git@gitee.com:gump12138/db-tutorial.git
gump12138
db-tutorial
db-tutorial
master

搜索帮助