0.前言
我们知道,redis是内存数据库,也就是说存放在redis中的数据是在内存里面的,这也是redis性能高的原因之一,但是存储在内存中的数据存在一个安全风险,那就是当redis服务重启的时候,数据会丢失,也就是存储在redis中的数据会丢失。为了解决该问题,redis有专门的数据持久化方案,包括RDB持久化和AOF持久化,今天我们就来看看这两种redis数据持久化的方案。
1.资源准备
我们需要一台安装好了redis的虚拟机来开始今天的实验,以下是资源信息:
操作系统 | IP地址 | CPU | 内存 | redis版本 |
---|---|---|---|---|
rocky9 linux | 192.168.159.167 | 4核 | 8G | 7.4 |
大家用其他配置的服务器也可以,只要安装有redis即可。
2.RDB持久化
RDB是redis database的缩写,也被称作redis数据快照,也就是将redis某一时刻的数据存储到磁盘中,所以快照中的值是早于或者等于内存中的值的。RDB持久化有两种方式,分别是手动触发和自动触发,下面我们来看下这两种触发方式。
2.1 手动触发RDB
在redis中使用两个命令可以触发RDB,分别是save和bgsave:
(1)save:触发RDB的同时会阻塞当前redis服务,直到RDB过程完成为止。
(2)bgsave:redis进程fork一个子进程,由子进程进行数据备份,不会造成redis服务阻塞,数据备份完成后子进程自动结束。
2.1.1 save命令
我们先来使用一下save命令,看下数据的恢复过程。
(1)连接redis
cd /usr/local/redis/bin/
./redis-cli
(2)创建测试数据
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> MSET x 1 y 2 c 3 d 4 e 5
OK
127.0.0.1:6379> MGET x y c d e
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
(3)执行数据备份
127.0.0.1:6379> save
OK
(4)停止服务并退出
127.0.0.1:6379> exit
systemctl stop redis
(5)重启redis
systemctl start redis
(6)检查数据
./redis-cli
127.0.0.1:6379> keys *
1) "c"
2) "d"
3) "y"
4) "e"
5) "x"
可以看到数据确实都还在,说明save持久化数据成功,并且我们可以在redis的data目录在看到一个rdb的文件,如下所示:
ll /usr/local/redis/data/
total 4
-rw-r--r--. 1 root root 118 Jun 9 22:15 dump.rdb
我的redis数据目录是在/usr/local/redis/data/,可以在配置文件查看。
2.1.2 bgsave命令
在执行bgsave操作之前,可以先将执行save命令产生的数据清理掉:
rm /usr/local/redis/data/dump.rdb
./redis-cli
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> keys *
(empty array)
(1)创建数据
127.0.0.1:6379> mset a 1 b 2 c 3 d 4 f 5 g 6
OK
127.0.0.1:6379> mget a b c d f g
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
6) "6"
(2)执行bgsave
127.0.0.1:6379> bgsave
Background saving started
(3)停止并退出服务
127.0.0.1:6379> exit
systemctl stop redis
(4)重启服务
systemctl start redis
(5)检查数据
./redis-cli
127.0.0.1:6379> keys *
1) "f"
2) "g"
3) "d"
4) "c"
5) "a"
6) "b"
可以看到数据没有丢失,再看下数据目录,确实也存在rdb文件:
ls /usr/local/redis/data/
dump.rdb
说明数据备份成功。
2.1.3 rdb持久化原理
因为save会阻塞redis服务,线上如果redis存储数据较多的话,save备份数据需要较长的时间,这会影响线上服务,所以推荐使用bgsave命令,这里也简单讲一下bgsave命令的原理。
(1)bgsave流程图
(2)bgsave具体流程
- redis客户端执行bgsave命令或者自动触发bgsave命令;
- 主进程判断当前是否已经存在正在执行的子进程,如果存在,那么主进程直接返回;
- 如果不存在正在执行的子进程,那么就fork一个新的子进程进行持久化数据,fork过程是阻塞的,fork操作完成后主进程即可执行其他操作(耗时很短,基本可忽略不计);
- 子进程先将数据写入到临时的rdb文件中,待快照数据写入完成后再原子替换旧的rdb文件;
- 同时发送信号给主进程,通知主进程rdb持久化完成,主进程更新相关的统计信息(info Persitence下的rdb_*相关选项)。
2.2 自动触发
在单节点的情况下,通常自动触发bgsave的方式有两种:
(1)在redis.conf中配置了save m n,即在m秒内有n次修改时,自动触发bgsave生成rdb文件;
(2)默认情况下执行shutdown命令时,如果没有开启aof持久化,那么也会触发bgsave操作。
今天我们主要讲通过设置redis配置文件的方式,来自动触发bgsave。
2.2.1 rdb周期性配置
在redis.conf配置文件中,rdb自动持久化周期性配置如下:
# 周期性执行条件的设置格式为
save <seconds> <changes>
# 默认的设置为:
save 3600 1 300 100 60 10000
# 以下设置方式为关闭RDB快照功能
save ""
redis 7.4版本默认配置变成了一行,但其实是三种持久化方式,这三种持久化方式是:
-
save 3600 1: 如果3600秒内有1条Key信息发生变化,则进行快照;
-
save 300 100: 如果300秒内有100条Key信息发生变化,则进行快照;
-
save 60 10000: 如果60秒内有10000条Key信息发生变化,则进行快照。 大家可以按照这个规则,根据自己的实际请求压力进行设置调整,如果要完全停止rdb持久化,就将save后面的字符串设置为空。
2.2.2 rdb其他配置
redis.conf中rdb持久化其他一些重要配置如下:
# 文件名称 dbfilename dump.rdb
# 文件保存路径 dir /usr/local/redis/data
# 如果持久化出错,主进程是否停止写入 stop-writes-on-bgsave-error yes
# 是否压缩 rdbcompression yes
# 导入时是否检查 rdbchecksum yes
2.2.3 自动触发实践
在测试之前,我们还是先讲之前生成的rdb文件清理一下:
rm /usr/local/redis/data/* -f
为了方便测试,我们设置触发策略为:
- 60s内key信息发生1次变化;
- 300s内key信息发生100次变化;
- 600s内key信息发生10000次变化。
(1)修改配置文件
save 60 1 300 100 600 10000
(2)重启redis
systemctl restart redis
(3)修改key值
./redis-cli
127.0.0.1:6379> keys *
1) "c"
2) "b"
3) "g"
4) "f"
5) "a"
6) "d"
127.0.0.1:6379> get a
"1"
127.0.0.1:6379> set a 10
OK
(4)检查rdb文件
ll /usr/local/redis/data/
total 4
-rw-r--r--. 1 root root 123 Jun 10 02:05 dump.rdb
可以看到rdb文件确实生成了,说明自动rdb持久化生效。
2.3 RDB优缺点
上面我们已经讲完了RDB持久化的基础知识,我们总结下RDB的优缺点。
2.3.1 优点
- RDB文件是某个时间节点的快照,默认使用LZF算法进行压缩,压缩后的文件体积远远小于内存大小,适用于备份、全量复制等场景;
- Redis加载RDB文件恢复数据速度较快,要远远优于AOF方式;
2.3.2 缺点
-
RDB方式实时性不够,无法做到秒级的持久化;
-
每次调用bgsave都需要fork子进程,fork子进程属于重量级操作,频繁执行成本较高;
-
RDB文件是二进制的,没有可读性;
-
版本兼容RDB文件问题;
3.AOF持久化
为了解决RDB无法做到秒级持久化这个问题,redis还提供了另外一种数据持久化的方案,也就是AOF持久化方案,redis的AOF持久化方案是一种"写后"日志,也就是redis先执行命令将数据写到内存中,之后再将数据记录到日志。
redis采用写后日志的原因:
- 保持高性能,避免额外的开销;
- 不会阻塞当前写操作。 写后日志的缺点:
- 如果命令执行完成,写日志之前宕机了,会丢失数据;
- 主线程写磁盘压力大,导致写盘慢,阻塞后续操作; 所以如果线上redis仅开启AOF持久化,就要注意一下以上问题了。
3.1 AOF回写策略
AOF日志记录redis的每个写命令,步骤分为:命令追加(append)、文件写入(write)和文件同步(sync)。
- 命令追加:当AOF持久化功能打开了,服务器执行完一个写入命令之后,会以协议格式将被执行的写命令追加到服务器的 aof_buf 缓冲区;
- 文件写入和同步:文件写入和同步就是将aof_buf缓冲区写入到AOF文件中。 至于何时将aof_buf缓冲区的内容写入AOF文件,redis提供了三种策略:
配置项 | 写回时机 | 优点 | 缺点 |
---|---|---|---|
Always | 同步写回 | 可靠性高,数据基本不丢失 | 每个写操作都要落盘,性能影响较大 |
Everysec | 每秒写回 | 性能适中 | 宕机丢失1秒的数据 |
No | 操作系统控制写回 | 性能好 | 宕机丢失的数据较多 |
以上三种策略可以根据业务需求以及存储在redis中数据的重要性进行选择。
3.2 AOF配置详解
默认情况下是没有开启AOF的,需要在redis.conf配置文件中设置,涉及到AOF的配置如下:
# appendonly参数开启AOF持久化
appendonly no
# AOF持久化的文件名,默认是appendonly.aof
appendfilename "appendonly.aof"
# AOF文件的保存位置和RDB文件的位置相同,都是通过dir参数设置的
dir ./
# 同步策略
# appendfsync always
appendfsync everysec
# appendfsync no
# aof重写期间是否同步
no-appendfsync-on-rewrite no
# 重写触发配置
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
# 加载aof出错如何处理
aof-load-truncated yes
# 文件重写策略
aof-rewrite-incremental-fsync yes
重要配置解析:
appendonly
:默认情况下AOF功能是关闭的,将该选项改为yes以便打开Redis的AOF功能;appendfsync
:这个参数项是AOF功能最重要的设置项之一,主要用于设置“真正执行”操作命令向AOF文件中同步的策略;no-appendfsync-on-rewrite
:always和everysec的设置会使真正的I/O操作高频度的出现,甚至会出现长时间的卡顿情况,这个问题出现在操作系统层面上,所有靠工作在操作系统之上的操作redis是没法解决的。为了尽量缓解这个情况,redis提供了这个设置项,保证在完成fsync函数调用时,不会将这段时间内发生的命令操作放入操作系统的Page Cache(这段时间Redis还在接受客户端的各种写操作命令)。auto-aof-rewrite-percentage
:在生产环境下,技术人员可以使用“BGREWRITEAOF”命令去重写AOF文件,但我们不能老是指望技术人员手动执行该操作,所以更多时候我们需要依靠redis中对AOF文件的自动重写策略。redis中对触发自动重写AOF文件的操作提供了两个设置:auto-aof-rewrite-percentage表示如果当前AOF文件的大小超过了上次重写后AOF文件的百分之多少后,就再次开始重写AOF文件。例如该参数值的默认设置值为100,意思就是如果AOF文件的大小超过上次AOF文件重写后的1倍,就启动重写操作。auto-aof-rewrite-min-size
:参考auto-aof-rewrite-percentage选项的介绍,auto-aof-rewrite-min-size设置项表示启动AOF文件重写操作的AOF文件最小大小。如果AOF文件大小低于这个值,则不会触发重写操作。注意,auto-aof-rewrite-percentage和auto-aof-rewrite-min-size只是用来控制redis中自动对AOF文件进行重写的情况,如果是技术人员手动调用"BGREWRITEAOF"命令,则不受这两个限制条件左右。
3.3 AOF持久化测试
在测试之前,我们将rdb文件删掉,并且清理掉之前测试用的数据:
rm /usr/local/redis/data/* -f
./redis-cli
[root@rocky-web bin]# ./redis-cli
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> keys *
(empty array)
修改redis.conf配置,修改项如下:
save "" // 完全关闭rdb持久化
dir /usr/local/redis/data // aof文件存放目录
appendonly yes // 开启redis的aof功能
appendfilename "appendonly.aof" // 文件名称
appenddirname "appendonlydir" // 目录名称
appendfsync everysec // 策略为每秒写文件
修改好之后重启redis:
systemctl restart redis
创建测试数据:
./redis-cli
127.0.0.1:6379> mset x 1 y 2 z 3
OK
127.0.0.1:6379> keys *
1) "x"
2) "z"
3) "y"
检查aof文件:
ll /usr/local/redis/data/appendonlydir/
total 12
-rw-r--r--. 1 root root 88 Jun 10 04:55 appendonly.aof.1.base.rdb
-rw-r--r--. 1 root root 79 Jun 10 04:55 appendonly.aof.1.incr.aof
-rw-r--r--. 1 root root 88 Jun 10 04:55 appendonly.aof.manifest
重启redis:
systemctl restart redis
查看redis数据:
./redis-cli
127.0.0.1:6379> keys *
1) "y"
2) "c"
3) "x"
aof文件生成了,重启redis服务之后数据也存在。
4.RDB和AOF混合使用
redis默认使用RDB的持久化策略,技术人员可以根据需要将持久化策略调整为AOF。在redis4.0版本之后,redis推出了RDB和AOF混合使用的方式,简单来说,RDB以一定的频率执行,而在两次快照之间,使用 AOF 日志记录这期间的所有命令操作。这个方法既能享受到 RDB 文件快速恢复的好处,又能享受到 AOF 只记录操作命令的简单优势, 实际环境中用的很多。
同时采用两种方式做持久化,数据恢复流程如下图所示:
-
redis重启时判断是否开启aof,如果开启了aof,那么就优先加载aof文件;
-
如果aof存在,那么就去加载aof文件,加载成功的话redis重启成功,如果aof文件加载失败,那么会打印日志表示启动失败,此时可以去修复aof文件后重新启动;
-
若aof文件不存在,那么redis就会转而去加载rdb文件,如果rdb文件不存在,redis直接启动成功;
-
如果rdb文件存在就会去加载rdb文件恢复数据,如加载失败则打印日志提示启动失败,如加载成功,那么redis重启成功,且使用rdb文件恢复数据。
接下来我们也来测试下开启两种持久化策略,看下效果。
(1)修改配置文件中的一些配置项
save 60 1 300 100 600 10000 // 开启rdb持久化策略
appendonly yes // 开启aof持久化策略
其实主要就是要开启两种策略,其他配置项,如文件位置,可以根据上面讲解的配置项含义自行修改。 (2)清理原有数据
rm /usr/local/redis/data/* -rf
./redis-cli
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> keys *
(empty array)
127.0.0.1:6379> exit
(3)重启服务
systemctl restart redis
(4)检查数据目录
ll /usr/local/redis/data/
total 4
drwxr-xr-x. 2 root root 103 Jun 10 05:31 appendonlydir
-rw-r--r--. 1 root root 88 Jun 10 05:31 dump.rdb
可以看到这个时候持久化文件就已经生成了,但目前还没有数据。 (5)创建测试数据
./redis-cli
127.0.0.1:6379> mset x 1 y 2 z 3
OK
127.0.0.1:6379> keys *
1) "y"
2) "x"
3) "z"
(6)再次重启服务
systemctl restart redis
(7)检查数据恢复
./redis-cli
127.0.0.1:6379> keys *
1) "z"
2) "x"
3) "y"
可以看到重启redis之后,数据也是存在的,说明持久化成功。
5.总结
今天我们讲了redis持久化的两种方案,分别是RDB和AOF,其中RDB是快照的持久化方式,记录当前时刻redis内存数据的情况,而AOF则是“写后”日志,也即先将数据先写到内存中,然后再落盘。两种持久化方式各有优劣,用户可以根据需要选择持久化方案。