Redis 的持久化

341 阅读16分钟

整体大纲

image.png

1、为什么要持久化

Redis 的读写都是在内存中,所以它的性能较高,但在内存中的数据会随着服务器的重启而丢失,为了保证数据不丢失,我们需要将内存中的数据存储到磁盘,以便 Redis 重启时能够从磁盘中恢复原有的数据,而整个过程就叫做 Redis 持久化。

Redis 持久化也是 Redis 和 Memcached 的主要区别之一,因为 Memcached 不具备持久化功能。

2、持久化的几种方式

Redis 持久化拥有以下三种方式:

  • 快照方式RDB, Redis DataBase)将某一个时刻 Redis 的内存数据,以二进制的方式写入磁盘。
  • 文件追加方式AOF, Append Only File),记录所有的操作命令,并以文本的形式追加到文件中。
  • 混合持久化方式Redis 4.0 之后新增的方式,混合持久化是结合了 RDBAOF 的优点,在写入的时候,先把当前的数据以 RDB 的形式写入文件的开头,再将后续的操作命令以 AOF 的格式存入文件,这样既能保证 Redis 重启时的速度,又能减低数据丢失的风险。

每种持久化方案,都有特定的使用场景

3、RDB

RDB(Redis DataBase)是将某一个时刻的内存快照(Snapshot),以二进制的方式写入磁盘的过程。

写入到磁盘的 RDB 文件名为 dump.rdb

快照的意思就是说:是某个数据的一个副本,或者说是一个拷贝。

触发方式

手动触发

手动触发持久化的操作有两个: savebgsave ,它们主要区别体现在:是否阻塞 Redis 主线程的执行。

(1)save 命令

在客户端中执行 save 命令,就会触发 Redis 的持久化,但同时也是使 Redis 处于阻塞状态,直到 RDB 持久化完成,才会响应其他客户端发来的命令,所以在生产环境一定要慎用

image-20220127123058179

(2)bgsave 命令

bgsave(background save),后台保存。

它会 fork() 一个子进程来执行持久化,整个过程中只有在 fork() 子进程时有短暂的阻塞,当子进程被创建之后,Redis 的主进程就可以响应其他客户端的请求了。

注:fork() 是一个系统调用函数,目的就是为了创建一个与原来进程几乎完全相同的进程。

显然,bgsave 更为适合我们使用。

image-20220127123010722

自动触发

(1)save m n

save m n 是指在 m 秒内,如果有 n 个键发生改变,则自动触发持久化。

例如,save 60 1 则表明在 60 秒内,至少有一个键发生改变,就会触发 RDB 持久化。

自动触发持久化,本质是 Redis 通过判断,如果满足设置的触发条件,自动执行一次 bgsave 命令

注意:当设置多个 save m n 命令时,满足任意一个条件都会触发持久化。

(2)flushall

flushall 命令用于清空 Redis 数据库,在生产环境下一定慎用。

Redis 执行了 flushall 命令之后,则会触发自动持久化,把 RDB 文件清空。

注意:这里清空的意思是指你本来就没有数据了,所以清空就相当于持久化。

(3)主从同步触发

Redis 主从复制中,当从节点执行全量复制操作时,主节点会执行 bgsave 命令,并将 RDB 文件发送给从节点,该过程会自动触发 Redis 持久化。

配置

Redis 中可以使用命令查询当前配置参数。查询命令的格式为:config get xxx ,例如,想要获取 RDB 文件的存储名称设置,可以使用 config get dbfilename

RDB 的配置参数在 Redis 配置文件中,也就是 redis.conf

# RDB 保存的条件
save 900 1
save 300 10
save 60 10000

# bgsave 失败之后,是否停止持久化数据到磁盘,yes 表示停止持久化,no 表示忽略错误继续写文件。
stop-writes-on-bgsave-error yes

# RDB 文件压缩
rdbcompression yes

# 写入文件和读取文件时是否开启 RDB 文件检查,检查是否有无损坏,如果在启动是检查发现损坏,则停止启动。
rdbchecksum yes

# RDB 文件名
dbfilename dump.rdb

# RDB 文件目录
dir ./

说明:

(1)save 参数

前面已经说过,例如 save 900 1,就是 900 秒内至少有一个键变化,则把内存中的数据持久化到硬盘上。

(2)rdbcompression 参数

Redis 会采用 LZF 算法进行压缩。如果不想消耗 CPU 性能来进行文件压缩的话,可以设置为关闭此功能,缺点是需要更多的磁盘空间来保存文件。

(3)rdbchecksum 参数

写入文件和读取文件时是否开启 RDB 文件检查,检查是否有无损坏,如果在启动是检查发现损坏,则停止启动。

改变 RDB 的配置,有两种方式

  • 手动修改 Redis 配置文件
  • 使用命令行设置,例如,使用 config set dir "/usr/data" 就是用于修改 RDB 的存储目录。

注意

手动修改 Redis 配置文件的方式是全局生效的,即重启 Redis 服务器设置参数也不会丢失,而使用命令修改的方式,在 Redis 重启之后就会丢失。

但手动修改 Redis 配置文件,想要立即生效需要重启 Redis 服务器,而命令的方式则不需要重启 Redis 服务器。

禁用持久化

禁用持久化可以提高 Redis 的执行效率,如果对数据丢失不敏感的情况下,可以在连接客户端的情况下,执行 config set save "" 命令即可禁用 Redis 的持久化。

RDB 文件恢复过程

Redis 服务器启动时,如果 Redis 根目录存在 RDB 文件 dump.rdbRedis 就会自动加载 RDB 文件恢复持久化数据。

如果根目录没有 dump.rdb 文件,请先将 dump.rdb 文件移动到 Redis 的根目录。 验证 RDB 文件是否被加载Redis 在启动时有日志信息,会显示是否加载了 RDB 文件。

注意:Redis 服务器在载入 RDB 文件期间,会一直处于阻塞状态,直到载入工作完成为止。

RDB 优缺点

RDB 优点

  • 占用容量小RDB 的内容为二进制的数据,占用容量更小,更紧凑,更适合做为备份文件。
  • 传输快(主从复制时是传输 RDB 文件来做的)RDB 对灾难恢复非常有用,它是一个紧凑的文件,可以更快的传输到远程服务器进行 Redis 服务恢复。
  • 提高运行速度RDB 可以更大程度的提高 Redis 的运行速度,因为每次持久化时 Redis 主进程都会 fork() 一个子进程,进行数据持久化到磁盘,Redis 主进程并不会执行磁盘 I/O 等操作。
  • 重启快。与 AOF 格式的文件相比,RDB 文件可以更快的重启。

RDB 缺点

  • 最新数据丢失。因为 RDB 只能保存某个时间间隔的数据,如果中途 Redis 服务被意外终止了,则会丢失一段时间内的 Redis 数据。
  • 数据集大时,fork() 系统调用占用时间长RDB 需要经常 fork() 才能使用子进程将其持久化在磁盘上。如果数据集很大,fork() 可能很耗时,并且如果数据集很大且 CPU 性能不佳,则可能导致 Redis 停止为客户端服务几毫秒甚至一秒钟。

4、AOF

AOF(Append Only File)中文是附加到文件,顾名思义 AOF 可以把 Redis 每个键值对操作都记录到文件(appendonly.aof)中。

触发方式

自动触发

有两种情况可以自动触发 AOF 持久化,分为是:

  • 满足 AOF 设置的策略触发

  • 满足 AOF 重写触发

AOF 持久化策略

AOF 持久化策略,分为以下三种:

  • always:每条 Redis 操作命令都会写入磁盘,最多丢失一条数据。
  • everysec:每秒钟写入一次磁盘,最多丢失一秒的数据。
  • no:不设置写入磁盘的规则,根据当前操作系统来决定何时写入磁盘,Linux 默认 30s 写入一次数据至磁盘。

这三种配置可以在 Redis 的配置文件(redis.conf)中设置:

# 开启每秒写入一次的持久化策略
appendfsync everysec

因为每次写入磁盘都会对 Redis 的性能造成一定的影响,所以要根据用户的实际情况设置相应的策略,一般设置每秒写入一次磁盘的频率就可以满足大部分的使用场景了。

AOF 文件重写

(1)什么是 AOF 重写?

AOF 重写指的是它会直接读取 Redis 服务器当前的状态,并压缩保存为 AOF 文件。

例如,我们增加了一个计数器,并对它做了 99 次修改,如果不做 AOF 重写的话,那么持久化文件中就会有 100 条记录执行命令的信息,而 AOF 重写之后,之后记录一条此计数器最终的结果信息,这样就去除了所有的无效信息。

(2)AOF 重写实现

触发 AOF 文件重写,要满足两个条件,这两个条件也是配置在 Redis 配置文件中的:

  • auto-aof-rewrite-min-size:允许 AOF 重写的最小文件容量,默认是 64mb
  • auto-aof-rewrite-percentageAOF 文件重写的大小比例,默认值是 100,表示 100%,也就是只有当前 AOF 文件,比最后一次(上次)的 AOF 文件大一倍时,才会启动 AOF 文件重写。

(3)AOF 重写流程

AOF 文件重写是生成一个全新的文件,并把当前数据的最少操作命令保存到新文件上,当把所有的数据都保存至新文件之后,Redis 会交换两个文件,并把最新的持久化操作命令追加到新文件上。

也就是说:生成新文件 -> 交换

手动触发

在客户端执行 bgrewriteaof 命令就可以手动触发 AOF 持久化。

配置

AOF 的配置参数在 Redis 的配置文件中,也就是 Redis 根路径下的 redis.conf 文件中,配置参数和说明如下:

# 是否开启 AOF,yes 为开启,默认是关闭
appendonly no

# AOF 默认文件名
appendfilename "appendonly.aof"

# AOF 持久化策略配置
# appendfsync always
appendfsync everysec
# appendfsync no

# AOF 文件重写的大小比例,默认值是 100,表示 100%,也就是只有当前 AOF 文件,比最后一次的 AOF 文件大一倍时,才会启动 AOF 文件重写。
auto-aof-rewrite-percentage 100

# 允许 AOF 重写的最小文件容量
auto-aof-rewrite-min-size 64mb

# 是否开启启动时加载 AOF 文件效验,默认值是 yes,表示尽可能的加载 AOF 文件,忽略错误部分信息,并启动 Redis 服务。
# 如果值为 no,则表示,停止启动 Redis,用户必须手动修复 AOF 文件才能正常启动 Redis 服务。
aof-load-truncated yes

AOF 文件恢复过程

正常数据恢复

正常情况下,只要开启了 AOF 持久化,并且提供了正常的 appendonly.aof 文件,在 Redis 启动时就会自定加载 AOF 文件并启动。

默认情况下 appendonly.aof 文件保存在 Redis 的根目录下。

持久化文件加载规则

  • 如果只开启了 AOF 持久化,Redis 启动时只会加载 AOF 文件(appendonly.aof),进行数据恢复。
  • 如果只开启了 RDB 持久化,Redis 启动时只会加载 RDB 文件(dump.rdb),进行数据恢复。
  • 如果同时开启RDBAOF 持久化,Redis 启动时只会加载 AOF 文件appendonly.aof),进行数据恢复。

AOF 开启的情况下,即使 AOF 文件不存在,只有 RDB 文件,也不会加载 RDB 文件。

image-20220127132025754

简单异常数据恢复

AOF 写入文件时如果服务器崩溃,或者是 AOF 存储已满的情况下,AOF 的最后一条命令可能被截断,这就是异常的 AOF 文件。

AOF 文件异常的情况下,如果为修改 Redis 的配置文件,也就是使用 aof-load-truncated 等于 yes 的配置,Redis 在启动时会忽略最后一条命令,并顺利启动 Redis

复杂异常数据恢复

AOF 文件可能出现更糟糕的情况,当 AOF 文件不仅被截断,而且中间的命令也被破坏,这个时候再启动 Redis 会提示错误信息并中止运行。

出现此类问题的解决方案如下:

  1. 首先使用 AOF 修复工具,检测出现的问题,在命令行中输入 redis-check-aof 命令,它会跳转到出现问题的命令行,这个时候可以尝试手动修复此文件。
  2. 如果无法手动修复,我们可以使用 redis-check-aof --fix 自动修复 AOF 异常文件,不过执行此命令,可能会导致异常部分至文件末尾的数据全部被丢弃。

AOF 优缺点

AOF 优点

  • AOF 持久化保存的数据更加完整AOF 提供了三种保存策略:每次操作保存、每秒钟保存一次、跟随系统的持久化策略保存,其中每秒保存一次,从数据的安全性和性能两方面考虑是一个不错的选择,也是 AOF 默认的策略,即使发生了意外情况,最多只会丢失 1s 钟的数据。

  • AOF 采用的是命令追加的写入方式,所以不会出现文件损坏的问题,即使由于某些意外原因,导致了最后操作的持久化数据写入了一半,也可以通过 redis-check-aof 工具轻松的修复。

  • AOF 持久化文件,非常容易理解和解析,它是把所有 Redis 键值操作命令,以文件的方式存入了磁盘。即使不小心使用 flushall 命令删除了所有键值信息,只要使用 AOF 文件,删除最后的 flushall 命令,重启 Redis 即可恢复之前误删的数据。

AOF 缺点

  • 对于相同的数据集来说,AOF 文件要大于 RDB 文件。
  • Redis 负载比较高的情况下,RDBAOF 性能更好。
  • RDB 使用快照的形式来持久化整个 Redis 数据,而 AOF 只是将每次执行的命令追加到 AOF 文件中,因此从理论上说,RDBAOF 更健壮。

5、混合持久化

RDB AOF 持久化各有利弊,RDB 可能会导致一定时间内的数据丢失,而 AOF 由于文件较大则会影响 Redis 的启动速度,为了能同时使用 RDBAOF 各种的优点,Redis 4.0 之后新增了混合持久化的方式。

在开启混合持久化的情况下,AOF 重写时会把 Redis 的持久化数据,以 RDB 的格式写入到 AOF 文件的开头,之后的数据再以 AOF 的格式化追加的文件的末尾

开启混合持久化

查询是否开启混合持久化可以使用 config get aof-use-rdb-preamble 命令。

yes 表示已经开启混合持久化,no 表示关闭,Redis 5.0 默认值为 yes

通过命令行开启

使用命令 config set aof-use-rdb-preamble yes

命令行设置配置的缺点是重启 Redis 服务之后,设置的配置就会失效。

通过修改 Redis 配置文件开启

Redis 的根路径下找到 redis.conf 文件,把配置文件中的 aof-use-rdb-preamble no 改为 aof-use-rdb-preamble yes

数据恢复和源码解析

混合持久化的数据恢复和 AOF 持久化过程是一样的,只需要把 appendonly.aof 放到 Redis 的根目录,在 Redis 启动时,只要开启了 AOF 持久化,Redis` 就会自动加载并恢复数据。

混合持久化的加载流程如下:

  1. 判断是否开启 AOF 持久化,开启继续执行后续流程,未开启执行加载 RDB 文件的流程。
  2. 判断 appendonly.aof 文件是否存在,文件存在则执行后续流程。
  3. 判断 AOF 文件开头是 RDB 的格式, 先加载 RDB 内容再加载剩余的 AOF 内容。
  4. 判断 AOF 文件开头不是 RDB 的格式,直接以 AOF 格式加载整个文件。

image-20220127133920541

Redis 判断 AOF 文件的开头是否是 RDB 格式的,是通过关键字 REDIS 判断的,RDB 文件的开头一定是 REDIS 关键字开头的,判断源码在 Redissrc/aof.c 中,核心代码如下所示:

char sig[5]; /* "REDIS" */
if (fread(sig,1,5,fp) != 5 || memcmp(sig,"REDIS",5) != 0) {
    // AOF 文件开头非 RDB 格式,非混合持久化文件
    if (fseek(fp,0,SEEK_SET) == -1) goto readerr;
} else {
    /* RDB preamble. Pass loading the RDB functions. */
    rio rdb;

    serverLog(LL_NOTICE,"Reading RDB preamble from AOF file...");
    if (fseek(fp,0,SEEK_SET) == -1) goto readerr;
    rioInitWithFile(&rdb,fp);
    // AOF 文件开头是 RDB 格式,先加载 RDB 再加载 AOF
    if (rdbLoadRio(&rdb,NULL,1) != C_OK) {
        serverLog(LL_WARNING,"Error reading the RDB preamble of the AOF file, AOF loading aborted");
        goto readerr;
    } else {
        serverLog(LL_NOTICE,"Reading the remaining AOF tail...");
    }
}
// 加载 AOF 格式的数据

可以看出 Redis 是通过判断 AOF 文件的开头是否是 REDIS 关键字,来确定此文件是否为混合持久化文件的。

AOF 格式的开头是 *,而 RDB 格式的开头是 REDIS

混合持久化优缺点

混合持久化优点

  • 混合持久化结合了 RDBAOF 持久化的优点,开头为 RDB 的格式,使得 Redis 可以更快的启动,同时结合 AOF 的优点,有减低了大量数据丢失的风险。

混合持久化缺点

  • AOF 文件中添加了 RDB 格式的内容,使得 AOF 文件的可读性变得很差;
  • 兼容性差,如果开启混合持久化,那么此混合持久化 AOF 文件,就不能用在 Redis 4.0 之前的版本了。

持久化最佳实践

控制持久化开关

使用者可根据实际的业务情况考虑,如果对数据的丢失不敏感的情况下,可考虑关闭 Redis 的持久化,这样所以的键值操作都在内存中,就可以保证最高效率的运行 Redis 了。 持久化关闭操作:

  • 关闭 RDB 持久化,使用命令: config set save ""
  • 关闭 AOF 和 混合持久化,使用命令: config set appendonly no

主从部署

使用主从部署,一台用于响应主业务,一台用于数据持久化,这样就可能让 Redis 更加高效的运行。

使用混合持久化

混合持久化结合了 RDBAOF 的优点,Redis 5.0 默认是开启的。

使用配置更高的机器

Redis 对 CPU 的要求并不高,反而是对内存和磁盘的要求很高,因为 Redis 大部分时候都在做读写操作,使用更多的内存和更快的磁盘,对 Redis 性能的提高非常有帮助。

参考

参考自《Redis 核心原理与实战》