Redis 持久化(persistence)技术口袋书

本文首发于 Redis 持久化(persistence)技术口袋书,转载请注明出处。

本文讲解 Redis 数据库的数据持久化解决方案。

测试环境:

RDB 和 AOF 持久化解决方案

Redis 提供两种持久化解决方案:RDB 持久化和 AOF 持久化。

要点:

RDB 持久化:可以在指定时间间隔内,生成数据集在这个时间点的快照。
AOF 持久化:通过记录服务器执行的所有写操作命令,在服务器重启时,通过重新执行这些命令来还原数据。

RDB 持久化方案

采用 RDB 持久化方案时,Redis 会每隔一段时间对数据集进行快照备份,换句话说这种方案在服务器发生故障时可能造成数据的丢失。所以,如果对数据的完整性有比较强烈的要求,可能不太适用这种备份方案,即它适用于做数据的备份。

执行持久化策略

  • 自动执行 RDB 持久化

我们已经知道,采用 RDB 持久化方案会每隔一段时间对数据进行备份,那么这个时间段如何确定呢?

我们可以到 redis.windows.conf 配置文件的 SNAPSHOTTING 配置节点获取答案,默认情况下 Redis 采用三种持久化策略:

save 900 1
save 300 10
save 60 10000

这里的 save 指令表示「在 x 秒内有 n 个及以上键被改动」则会自动保存一次数据集,比如配置中的 save 60 10000 表示如果在 60 秒内有 10000 个及以上的键被改动时则执行保存数据集操作。

我们在启动 Redis 服务时,服务器会读取配置文件中的配置,所以 RDB 持久化策略会自动启动,当满足条件时会执行持久化处理。

  • 手动执行 RDB 持久化

不过,有时我们可能需要手动的执行 RDB 持久化处理,那么 Redis 有没有提供类似的方法呢?

答案是有的,我们可以使用 [SAVE]](http://redisdoc.com/server/save.html)(这里不是配置文件中的 save 指令)BGSAVE 命令,来手动执行 RDB 持久化处理。

虽然,savebgsave 都可以手动的执行 RDB 持久化处理。但是它们的工作模式完全不同。

  1. 执行 SAVE 命令时,会阻塞 Redis 主进程,直到保存完成为止。在主进程阻塞期间,服务器不能处理客户端的任何请求。
  2. BGSAVE 则 fork 出一个子进程,子进程负责执行保存处理,并在保存完成之后向主进程发送信号,通知主进程保存完成。所以 Redis 服务器在 BGSAVE 执行期间仍然可以继续处理客户端的请求。

注意:虽然通过 SAVE 命令可以执行 RDB 持久化处理,但是它的运行原理同自动持久化中的 save 指令是完全不同的,save 指令的工作原理同 BGSAVE 指令。

快照(SNAPSHOTTING)

在 RDB 持久化策略中,我们引入了「快照」的概念,即「在 x 秒内有 n 个及以上键被改动」则执行持久化处理。

写时复制(copy-on-write):快照的运行原理

  1. Redis 调用 fork() ,同时拥有父进程和子进程。
  2. 子进程将数据集写入到一个临时 RDB 文件中。
  3. 当子进程完成对新 RDB 文件的写入时,Redis 用新 RDB 文件替换原来的 RDB 文件,并删除旧的 RDB 文件。

摘自 Redis 持久化

优点

  • RDB 是一个非常紧凑(compact)的文件(笔者注:因为 RDB 持久化文件 dump.rdb 将数据集以二级制形式保存),它保存了 Redis 在某个时间点上的数据集。 这种文件非常适合用于进行备份。
  • RDB 非常适用于灾难恢复(disaster recovery):它只有一个文件,并且内容都非常紧凑,可以(在加密后)将它传送到别的数据中心,或者亚马逊 S3 中。
  • RDB 可以最大化 Redis 的性能:父进程在保存 RDB 文件时唯一要做的就是 fork 出一个子进程,然后这个子进程就会处理接下来的所有保存工作,父进程无须执行任何磁盘 I/O 操作。
  • RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。

缺点

  • 可能在服务器故障时导致数据丢失,因为 RDB 采用的是定时保存数据的机制,所以可能导致下次保存数据时的数据丢失。
  • 可能导致服务器无法处理客户端处理,这是由于 RDB 执行非阻塞(BGSAVE 或 save 指令)保存时,会 fock 出子进程,如果待保存的数据集非常大可能会非常耗时。

AOF 持久化方案

通过 RDB 持久化方案的学习,我们知道它可能导致数据丢失,如果你的项目忍不了数据丢失的问题,那么可能就需要使用 AOF 持久化方案。

AOF(append only file):只进行追加操作的文件。默认情况下,Redis 会禁用 AOF 重写,无需开启我们需要到配置文件中将 appendonly 指令配置为 yes(默认:no 不启用)

启用 AOF 持久化方案后,当我们执行类似 SET 设置(或修改)命令时,Redis 会将命令以 Redis 通信协议 文本保存到 appendonly.aof 文件中。

执行持久化策略

AOF 持久化方案提供 3 种不同时间策略将数据同步到磁盘中,同步策略通过 appendfsync 指令完成:

  • everysec(默认):表示每秒执行一次 fsync 同步策略,效率上同 RDB 持久化差不多。由于每秒同步一次,所以服务器故障时会丢失 1 秒内的数据。
  • always: 每个写命令都会调用 fsync 进行数据同步,最安全但影响性能。
  • no: 表示 Redis 从不执行 fsync,数据将完全由内核控制写入磁盘。对于 Linux 系统来说,每 30 秒写入一次。

使用是推荐采用默认的 everysec 每秒同步策略,兼顾安全与效率。

写时复制(copy-on-write):AOF 持久化的运行原理

  1. Redis 主进程执行 fork() 创建出子进程。
  2. 子进程开始将新 AOF 文件的内容写入到临时文件。
  3. 对于所有新执行的写入命令,父进程一边将它们累积到一个内存缓存中,一边将这些改动追加到现有 AOF 文件的末尾: 这样即使在重写的中途发生停机,现有的 AOF 文件也还是安全的。
  4. 当子进程完成重写工作时,它给父进程发送一个信号,父进程在接收到信号之后,将内存缓存中的所有数据追加到新 AOF 文件的末尾。
  5. 搞定!现在 Redis 原子地用新文件替换旧文件,之后所有命令都会直接追加到新 AOF 文件的末尾。

摘自 Redis 持久化

优化 AOF 备份文件

我们知道 AOF 的运行原理是不断的将写入的命令以 Redis 通信协议的数据格式追加到 .aof 文件末尾,这就会导致文件的体积不断增大。

如果所有的命令完全不同到没有关系。

但是,如果命令处理类似计数器的功能,比如执行 100 次 INCR(incr counter) 处理,AOF 文件会保存全部的 INCR 命令的执行记录,但实际上我们知道这些处理的结果同 set counter 100 并无二致。这就导致我们的 .aof 多存储了 99 条命令记录。

这时,我们就可以使用 Redis 提供的 BGREWRITEAOF 重写命令,将 AOF 文件进行重写优化。

举例:

SET name 'liugongzi'
SET age 18
SET name 'liugongzi handsome'

AOF 文件将这些写入命令保存到(appendonly.aof)文件中,内容如下:

*2
$6
SELECT
$1
0
*3
$3
set
$4
name
$9
liugongzi
*3
$3
set
$3
age
$2
18
*3
$3
set
$4
name
$18
liugongzi handsome

写入的内容完全遵循 Redis 通信协议。通过示例,我们知道虽然我们执行了两次 set name 操作,但最终 Redis 保存的 name 值是 liugongzi handsome。也就是说第一次 set name 其实并无必要。

现在我们通过 BGREWRITEAOF 命令对文件进行重写处理:

127.0.0.1:6380> BGREWRITEAOF
Background append only file rewriting started

重写完成后的 AOF 文件内容如下:

*2
$6
SELECT
$1
0
*3
$3
SET
$3
age
$2
18
*3
$3
SET
$4
name
$18
liugongzi handsome

通过对比重写前后的文件内容,可以发现 Redis 将第一次的 set name 'liugongzi' 操作给删出掉了。这样就达到优化 AOF 文件的目的。

补充一句 AOF 重写,并不是对 AOF 文件进行重写,而是依据 Redis 在内存中当前的键值进行重写的。

优点

  • 提供比 RDB 持久化方案更安全的数据,由于默认采用每秒进行持久化处理,所有即使服务器重启或宕机,最多也就丢失 1 秒内的数据。
  • AOF 文件有序地保存了对数据库执行的所有写入操作, 这些写入操作以 Redis 协议的格式保存, 因此 AOF 文件的内容非常容易被人读懂, 对文件进行分析(parse)也很轻松

缺点

  • 相比于 RDB 持久化,AOF 文件会比 RDB 备份文件大得多。
  • AOF 持久化的速度可能比 RDB 持久化速度慢。
  • AOF 在过去曾经发生过这样的 bug : 因为个别命令的原因,导致 AOF 文件在重新载入时,无法将数据集恢复成保存时的原样。 (举个例子,阻塞命令 BRPOPLPUSH 就曾经引起过这样的 bug 。) 测试套件里为这种情况添加了测试: 它们会自动生成随机的、复杂的数据集, 并通过重新载入这些数据来确保一切正常。 虽然这种 bug 在 AOF 文件中并不常见, 但是对比来说, RDB 几乎是不可能出现这种 bug 的。

摘自 Redis 持久化

Redis 数据恢复

通过前面的学习我们了解到 Redis 是如何执行 RDB 和 AOF 持久化处理的,现在我们简单了解下 Redis 是如何恢复 RDB 或 AOF 备份中的数据。

我们知道 Redis 是一种内存型的 NoSQL 数据库(或者说数据结构),当服务重启或宕机都会导致内存中的数据丢失。

所以,当 Redis 服务器重启或恢复时,它会进行读取 RDB 或 AOF 文件(如果存在的话)处理,将文件中的数据重新载入内存实现数据恢复操作。

Redis 数据恢复采用两套恢复方案:

  • 开启 AOF 持久化方案时,优先采用 AOF 文件进行数据恢复

这个很好理解,因为 AOF 持久化方案的数据保存是秒级的,所以相对于 RDB 持久化数据更完整,所以在启动 Redis 服务器是,会在 AOF 启用时有限载入 AOF 文件进行数据还原。

  • 未开启 AOF 持久化方案是,Redis 通过载入 RDB 文件进行数据恢复

RDB 持久化配置

到这里,相信你对 Redis 持久化已经有了相当大了解了,这节开始我们将学习 Redis 配置文件,看看如何使用 RDB 和 AOF 持久化功能。

Redis 服务器配置文件默认是 redis.windows.conf

RDB 持久化配置选项

RDB 配置位于 SNAPSHOTTING 配置节点。

  • 开启 / 关闭 RDB 持久化功能
  1. 严格来说 Redis 没有提供类似 AOF 的 appendonly 指令来开启 RDB 持久化功能,我们可以通过注释掉 save 指令来关闭 RDB 备份方案。
#save 900 1
#save 300 10
#save 60 10000
  1. 或者使用 config set save "" 命令来关闭重写,但是如果仅使用这条命令,仅在当前服务器运行时生效,所以重启服务器依然从配置文件读取 RDB 重写规则。如果想永久生效,可以运行 config rewrite 命令,将 config set save 命令结果写入到配置文件。执行完 config rewrite 命令后会直接删除 redis.windows.conf 配置中的 save 指令。
  • 是否启用压缩

通过 rdbcompression 指令完成,默认 yes 进行压缩。

  • 修改备份文件名

使用 dbfilename 指令,默认值 dump.rdb

  • 修改备份文件存储目录

使用 dir 指令,默认值 your_redis_path。另外 AOF 备份数据同样会保存到该目录下。

AOF 持久化配置选项

AOF 配置位于 APPEND ONLY MODE 配置节点。

  • 开启 / 关闭 AOF 持久化功能

开启 AOF 持久化功能,通过 appendonly 指令完成,取值范围 yes / no,默认:no 不开启 AOF 重写。

  • 修改备份文件名

appendfilename 指令完成,默认值 appendonly.aof

  • 设置持久化执行策略

请参考前文 appendfsync 指令说明。

  • AOF 备份文件重写规则配置

之前我们通过使用命令 BGREWRITEAOF 对 AOF 执行重写,但是当我们启用 AOF 持久化功能后,Redis 默认会启用 AOF 重写优化,这个工作有两条指令完成:

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

auto-aof-rewrite-percentage 指令表示,本次执行 AOF 重写时,当 AOF 文件的大小是上次执行重写时文件的百分之多少才可以自动重写。默认: 100 表示本次重写时的 AOF 文件是上次 2 倍可以自动重写。

auto-aof-rewrite-min-size 这个指令用于设置进行 AOF 文件自动重写的最小文件大小。

换言之,这两条配置表示:当 AOF 文件大小达到 64mb 时,才开始自动进行重写。下一次只有当文件大小需达到 128 mb 才能再次重写,以此类推。

  • 自动修复出错的 AOF 数据

当我们的 Redis 服务器宕机时,可能导致 AOF 文件的尾部数据不完整,在重启 Redis 服务器可能导致数据不一致。此时可以通过:

aof-load-truncated 指令在启动 Redis 自动修复文件。它的取值范围是 yes / no,默认为 yes 重启时自动修复。

同样的我们也可以通过 redis-check-aof --fix 修复工具手动进行修复。

参考资料

手册 - 持久化

Redis 设计与实现 - RDB

Redis 设计与实现 - AOF

[Redis 持久化解密]part 1 part 2 原文

How to disable Redis RDB?

Redis 详解(七)------ AOF 持久化

NoSQL 之【Redis】学习(三):Redis 持久化 Snapshot 和 AOF 说明

Redis 配置文件 redis.conf 项目详解

本作品采用《CC 协议》,转载必须注明作者和本文链接
本帖由系统于 5年前 自动加精
liuqing_hu
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
讨论数量: 4
liuqing_hu

@Herjew 会有后台 save 进程执行持久化操作。即 60s 内有超过 1w 键被更新,执行保存;否则,达到后台 save 执行时间,同样执行持久化。

4年前 评论
疾风刺火 4年前
wanghan

真好,学习了

5年前 评论

迁移的时候不是直接复制dump.rdb 或appendonly.aof过去就把数据迁移过去了。

5年前 评论

关于 RDB 持久化,文中举例:

比如配置中的 save 60 10000 表示如果在 60 秒内有 10000 个及以上的键被改动时则执行保存数据集操作。

那如果 60 秒内有 小于 10000 个及以下的键被改动时的数据怎么办?不会不保存了吧?

4年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!