Redis-持久化方式小白级详解

421 阅读7分钟

Redis作为最常用的缓存中间件,持久化使其非常重要的一环,目前大型互联网公司几乎都依赖于Redis作为缓存层,用来减轻数据库的压力。那么我们有必要详细的了解一下Redis的持久化策略。

RDB快照

RDB持久化是其中一种持久化方式,可以简单的理解为,在满足某些条件后,redis会自动的将缓存中的所有数据打包成一个文件放到指定目录中,可以在redis的配置文件redis.conf中找到这些配置:

//指定生成的快照文件的文件名,一般就是dump.rdb
dbfilename "dump.rdb"

//指定生成快照文件的目录位置
dir "/usr/local/redis-5.0.9/data/6379/"

//下面三行命令是RDB持久化策略
//1. 900秒内有至少有1个键被改动
save 900 1
//2. 300秒内有至少有10个键被改动
save 300 10
//3. 60秒内有至少有1000个键被改动
save 60 10000
//以上三个策略只要满足其中一个,redis后台就会自动将缓存中的数据生成dump.rdb文件保存到磁盘中
//如果想关闭RDB只需要将上面三行save注释掉就可以了。

以上是redis自动帮我们生成dump.rdb文件,其实我们自己手动调用命令也可以主动触发生成dump.rdb文件。
进入redis客户端执行命令savebgsave可以生成dump.rdb文件,每次命令执行都会将所有redis内存快照到一个新的rdb文件里,并覆盖原有rdb快照文件。save是同步命令,bgsave是异步命令,bgsave会从redis主进程fork(fork()是linux函数)出一个子进程专门用来生成rdb快照文件。 save与bgsave对比: 我们可以看下生成的dump.rdb文件是什么样子的: dump.rdb文件里面被压缩成二进制文件了,所以看起来都是乱码。
RDB持久化方式看起来有一个巨大的缺陷,这种持久化方式看起来不太安全,例如:我在第59秒的时候执行了999个命令,结果59.5秒的时候Redis服务器挂了,这样我的这999条命令就完全丢了。那么Redis为了解决这个问题还有另外一种持久化方式。

AOF持久化

从 1.1 版本开始, Redis 增加了一种完全耐久的持久化方式: AOF 持久化,将修改的每一条指令记录进文件appendonly.aof中,你可以通过如下命令打开AOF持久化方式:

appendonly yes

redis会在磁盘上帮我们生成一个appendonly文件

// aof持久化生成的文件名
appendfilename "appendonly.aof"
//与上面RDB一样,这里是指定生成文件目录
dir "/usr/local/redis-5.0.9/data/6379/"

我们先来运行几条命令:

127.0.0.1:6379> set zzz1 666
OK
127.0.0.1:6379> set zzz2 666
OK
127.0.0.1:6379> set zzz3 666
OK
127.0.0.1:6379> set zzz4 666
OK
127.0.0.1:6379> set zzz5 666

然后我们打开appendonly.aof文件看看是什么: 如上图所示,红色框里面,好像就是我执行的后四条set命令。所以这种持久化方式就是将我们执行的所有修改的命令一条条的保存了起来,类似于mysql的binlog。

AOF是基于resp协议来保存文件:
*3 //代表 (set zzz5 666) 这条命令有三个参数
3//代表set有三个字符set3 //代表 set 有三个字符 set 4 //代表 zzz5 有四个字符
zzz5
$3 //代表 666 有三个字符
666
所以以上所有命令就可以认为是执行了一条 “set zzz5 666”

AOF的持久化策略又是如何配置的呢?还是在redis.conf里面:

//以下是三个AOF持久化策略,只能三选一

//1. 每次有新命令追加到appendonly文件中,非常慢,也非常安全。
appendfsync always
//2. 每秒将这一秒执行的命令一次性追加到appendonly文件中,故障时只会丢失 1 秒钟的数据
appendfsync everysec
//3. 这里不是不写appendonly文件,而是将数据交给操作系统来处理。更快,也更不安全的选择。
appendfsync no
//一般默认使用第二种模式,以兼顾速度和安全性。

这种持久化方式相较于RDB来说看起来安全很多,但是感觉记录的数据是不是有点重复了?我举个例子,运行了如下命令:

//将key x 自增了4次
127.0.0.1:6379> incr x  
(integer) 1
127.0.0.1:6379> incr x  
(integer) 2
127.0.0.1:6379> incr x  
(integer) 3
127.0.0.1:6379> incr x  
(integer) 4
// 可以看到x的值是4
127.0.0.1:6379> get x
"4"

那么我们看看appendonly文件里面是如何保存的: 直接就保存了4条incr命令,那我到时候利用这个文件数据还原的时候,岂不是要一条一条的再重新执行一遍??这不是太慢了吗!其实我们期望的结果就是x的value=4就可以了。
其实Redis设计了一套AOF重写机制,目的就是把appendonly文件中的命令做一次合并。

AOF重写
如下两个配置可以控制AOF自动重写频率:

//aof文件至少要达到64M才会自动重写,文件太小恢复速度本来就很快,重写的意义不大
# auto-aof-rewrite-min-size 64mb 
//aof文件自上一次重写后文件大小增长了100%则再次触发重写
# auto-aof-rewrite-percentage 100 

当然AOF还可以手动重写,进入redis客户端执行命令bgrewriteaof重写AOF文件,这个命令也是会会fork出一个子进程去做,不会对redis正常命令处理有太多影响。

RDB和AOF的比较 Redis启动时如果既有rdb文件又有aof文件则优先选择aof文件恢复数据,因为aof一般来说数据更全一点。
但是成人的世界就是我全都要,那么有没有数据又安全,体积又小,恢复速度又快的呢??当然有,把这两个合起来不久完美了。

混合持久化

重启 Redis 时,我们很少使用 RDB来恢复内存状态,因为会丢失大量数据。我们通常使用 AOF 日志重放,但是重放 AOF 日志性能相对 RDB来说要慢很多,这样在 Redis 实例很大的情况下,启动需要花费很长的时间。 Redis 4.0 为了解决这个问题,带来了一个新的持久化选项——混合持久化
通过如下配置可以开启混合持久化:

aof-use-rdb-preamble yes

如果开启了混合持久化,在AOF重写时,不再是单纯将内存数据转换为RESP命令写入AOF文件,而是将重写这一刻之前的内存做RDB快照处理,并且将RDB快照内容和增量的AOF修改内存数据的命令存在一起,都写入新的AOF文件,新的文件一开始不叫appendonly.aof,等到重写完新的AOF文件才会进行改名,原子的覆盖原有的AOF文件,完成新旧两个AOF文件的替换。于是在 Redis 重启的时候,可以先加载 RDB 的内容,然后再重放增量AOF日志就可以完全替代之前的AOF全量文件重放,因此重启效率大幅得到提升。
这种方式下appendonly.aof里面的内容就不仅仅是resp命令了,而是如下图所示: 我们直接看例子比较清晰,我们先执行几条命令:

127.0.0.1:6379> set qqqqq 1
OK
127.0.0.1:6379> set qqqqq 2
OK
127.0.0.1:6379> set qqqqq 3
OK

打开appendonly.aof 文件看看: 文件被分为了两个部分了,一部分是RDB格式的,一部分是resp格式的。那么这个时候我们调用bgrewriteaof执行一遍AOF重写呢? 可以看到文件里面只有RDB格式的二进制文件内容了,这个时候我又添加一条命令。

127.0.0.1:6379> set qqqqq 4
OK

那么appendonly.aof文件肯定又变为两部分了:

总结

我们了解了3种Redis的持久化方式,如果使用的Redis4.0以上,那么尽量使用混合持久化方式,如果用的Redis4.0以下,优先选择AOF的方式保证数据的安全。