整体大纲
1、为什么要持久化
Redis 的读写都是在内存中,所以它的性能较高,但在内存中的数据会随着服务器的重启而丢失,为了保证数据不丢失,我们需要将内存中的数据存储到磁盘,以便 Redis 重启时能够从磁盘中恢复原有的数据,而整个过程就叫做 Redis 持久化。
Redis 持久化也是 Redis 和 Memcached 的主要区别之一,因为 Memcached 不具备持久化功能。
2、持久化的几种方式
Redis 持久化拥有以下三种方式:
- 快照方式(
RDB, Redis DataBase)将某一个时刻Redis的内存数据,以二进制的方式写入磁盘。 - 文件追加方式(
AOF, Append Only File),记录所有的操作命令,并以文本的形式追加到文件中。 - 混合持久化方式,
Redis 4.0之后新增的方式,混合持久化是结合了RDB和AOF的优点,在写入的时候,先把当前的数据以RDB的形式写入文件的开头,再将后续的操作命令以AOF的格式存入文件,这样既能保证Redis重启时的速度,又能减低数据丢失的风险。
每种持久化方案,都有特定的使用场景。
3、RDB
RDB(Redis DataBase)是将某一个时刻的内存快照(Snapshot),以二进制的方式写入磁盘的过程。
写入到磁盘的 RDB 文件名为 dump.rdb。
快照的意思就是说:是某个数据的一个副本,或者说是一个拷贝。
触发方式
手动触发
手动触发持久化的操作有两个: save 和 bgsave ,它们主要区别体现在:是否阻塞 Redis 主线程的执行。
(1)save 命令
在客户端中执行 save 命令,就会触发 Redis 的持久化,但同时也是使 Redis 处于阻塞状态,直到 RDB 持久化完成,才会响应其他客户端发来的命令,所以在生产环境一定要慎用。
(2)bgsave 命令
bgsave(background save),后台保存。
它会 fork() 一个子进程来执行持久化,整个过程中只有在 fork() 子进程时有短暂的阻塞,当子进程被创建之后,Redis 的主进程就可以响应其他客户端的请求了。
注:fork() 是一个系统调用函数,目的就是为了创建一个与原来进程几乎完全相同的进程。
显然,bgsave 更为适合我们使用。
自动触发
(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.rdb,Redis 就会自动加载 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-percentage:AOF文件重写的大小比例,默认值是 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),进行数据恢复。 - 如果同时开启了
RDB和AOF持久化,Redis启动时只会加载AOF文件(appendonly.aof),进行数据恢复。
在 AOF 开启的情况下,即使 AOF 文件不存在,只有 RDB 文件,也不会加载 RDB 文件。
简单异常数据恢复
在 AOF 写入文件时如果服务器崩溃,或者是 AOF 存储已满的情况下,AOF 的最后一条命令可能被截断,这就是异常的 AOF 文件。
在 AOF 文件异常的情况下,如果为修改 Redis 的配置文件,也就是使用 aof-load-truncated 等于 yes 的配置,Redis 在启动时会忽略最后一条命令,并顺利启动 Redis。
复杂异常数据恢复
AOF 文件可能出现更糟糕的情况,当 AOF 文件不仅被截断,而且中间的命令也被破坏,这个时候再启动 Redis 会提示错误信息并中止运行。
出现此类问题的解决方案如下:
- 首先使用
AOF修复工具,检测出现的问题,在命令行中输入redis-check-aof命令,它会跳转到出现问题的命令行,这个时候可以尝试手动修复此文件。 - 如果无法手动修复,我们可以使用
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负载比较高的情况下,RDB比AOF性能更好。 RDB使用快照的形式来持久化整个Redis数据,而AOF只是将每次执行的命令追加到AOF文件中,因此从理论上说,RDB比AOF更健壮。
5、混合持久化
RDB 和 AOF 持久化各有利弊,RDB 可能会导致一定时间内的数据丢失,而 AOF 由于文件较大则会影响 Redis 的启动速度,为了能同时使用 RDB 和 AOF 各种的优点,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` 就会自动加载并恢复数据。
混合持久化的加载流程如下:
- 判断是否开启
AOF持久化,开启继续执行后续流程,未开启执行加载RDB文件的流程。 - 判断
appendonly.aof文件是否存在,文件存在则执行后续流程。 - 判断
AOF文件开头是RDB的格式, 先加载RDB内容再加载剩余的AOF内容。 - 判断
AOF文件开头不是RDB的格式,直接以AOF格式加载整个文件。
Redis 判断 AOF 文件的开头是否是 RDB 格式的,是通过关键字 REDIS 判断的,RDB 文件的开头一定是 REDIS 关键字开头的,判断源码在 Redis 的 src/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。
混合持久化优缺点
混合持久化优点
- 混合持久化结合了
RDB和AOF持久化的优点,开头为RDB的格式,使得Redis可以更快的启动,同时结合AOF的优点,有减低了大量数据丢失的风险。
混合持久化缺点
AOF文件中添加了RDB格式的内容,使得AOF文件的可读性变得很差;- 兼容性差,如果开启混合持久化,那么此混合持久化
AOF文件,就不能用在Redis 4.0之前的版本了。
持久化最佳实践
控制持久化开关
使用者可根据实际的业务情况考虑,如果对数据的丢失不敏感的情况下,可考虑关闭 Redis 的持久化,这样所以的键值操作都在内存中,就可以保证最高效率的运行 Redis 了。 持久化关闭操作:
- 关闭
RDB持久化,使用命令:config set save "" - 关闭
AOF和 混合持久化,使用命令:config set appendonly no
主从部署
使用主从部署,一台用于响应主业务,一台用于数据持久化,这样就可能让 Redis 更加高效的运行。
使用混合持久化
混合持久化结合了 RDB 和 AOF 的优点,Redis 5.0 默认是开启的。
使用配置更高的机器
Redis 对 CPU 的要求并不高,反而是对内存和磁盘的要求很高,因为 Redis 大部分时候都在做读写操作,使用更多的内存和更快的磁盘,对 Redis 性能的提高非常有帮助。
参考
参考自《Redis 核心原理与实战》