Redis_13_持久化(2)
回顾RDB
上篇文章,我们讲了RDB是Redis中的一个定期备份(把数据以文件的方式写入硬盘)的机制,它具有很多的优势:
- RDB是一个紧凑压缩的二进制文件,代表Redis在某个时间点上的数据快照,非常适用于备份,全量复制等场景,比如:每6小时执行bgsave备份,并把RDB文件复制到远程机器或者文件系统上(如hdfs)用于灾备。
- Redis加载RDB恢复数据远远快于AOF的方式。(RDB这里使用二进制的方式来组织数据,直接把数据读取到内存中,按照字节的格式取出来,放到结构体/对象中即可;而AOF则是使用文本的方式来组织数据,则需要进行一系列的字符串切分操作~~)
- RDB方式数据没办法做到实时持久化,因为bgsave每次运行都要执行fork创建子进程,属于重量级操作,频繁执行成本过高。
- RDB文件使用特定二进制格式保存,Redis版本演进过程中有多个RDB版本,兼容性可能有风险。
RDB也有它的劣势,它最大的问题在于不能实时的持久化保存数据,在两次生成快照之间,实时地数据可能会随着重启而丢失~~
AOF
基本使用
刚才讲到RDB不能实时的持久化保存数据,而我们下面要讲的AOF就能很好地解决这个问题。
AOF类似于MySQL的binlog,它会把用户的每个操作,都记录到文件中。
当开启AOF的时候,RDB就不生效了;启动的时候不再读取RDB文件内容了~~当Redis重新启动的时候,就会读取这个AOF文件中的内容,用来恢复数据~~
AOF默认一般是关闭状态,修改配置文件,来开启AOF功能~~
我们将这里的AOF设为一个开启:
![]()
然后重启redis服务器:
进入redis客户端,创建几个key:
进入var/lib/redis路径下,可以看到两个文件:RDB文件和AOF文件(当这两个文件同时存在,会优先加载AOF文件)

vim进入AOF文件中,可以看到:
AOF是一个文本文件,每次进行的操作,都会被记录到文本文件中,通过一些特殊符号作为分隔符,来对命令的细节做出区分~~(具体分隔符的规则,不必研究)。
AOF是否会影响Redis性能?
我们知道:Redis虽然是一个单线程的服务器,但是速度很快~~
为啥速度快?重要原因,只是操作内存~~而引入AOF之后,又要写内存,又要写硬盘~~还能喝之前一样快了嘛?
实际上是没有影响的!AOF并没有影响到redis处理请求的速度~~
1、AOF机制并非是直接让工作线程把数据写入硬盘,而是先写入一个内存中的缓冲区,积累一波之后,再统一写入硬盘。
写入缓冲区这个操作,就大大降低了写硬盘的次数~~
假设有100个请求,一次写入硬盘。比分100次,每次写入一个请求要快很多!!
写硬盘的时候,写入硬盘数据的多少,对于性能影响没有很大,但是写入硬盘的次数则影响很大了~~
2、硬盘上读写数据,顺序读写的速度是比较快的(还是比内存要慢很多),随机访问则速度是比较慢的~~
AOF是每次把新的到左写入到源文件的末尾,属于顺序写入。
那就有疑问了:如果把数据写入到缓冲区里,本质还是再内存中呀~~万一这个时候,突然进程挂了,或者主机掉电了,咋办??是不是缓冲区的数据就丢了??
是的!!缓冲区还没来得及写入硬盘中的数据是会丢的~
这时候就需要我们做一些取舍了,就像MySQL事务中的隔离级别那样。
Redis给出了一些选项,让程序猿根据实际情况来决定怎么取舍~~缓冲区的刷新策略~~
刷新频率越高,性能影响越大,同时数据的可靠性就越高,刷新频率越低,性能影响越小,数据可靠性就越低~~

这个配置也是在redis配置文件中可以调整的,默认是everysec。
AOF的重写机制
我们知道当AOF文件和RDB文件同时存在时,会优先从AOF文件中进行加载。而AOF文件不仅要记载数据的最终结果还要记载中间的操作(冗余的内容),随着AOF文件的持续增长,体积越来越大~会影响到redis下次启动时间~~
因此redis就存在一个机制,能够对aof文件进行整理操作。这个整理就能够剔除其中的冗余操作,并且合并一些操作,达到给aof文件瘦身这样的效果。上述内容就是AOF的重写机制!!
例如下列操作:
AOF重写的触发方式
自动触发的配置:

AOF重写的流程
AOF重写的流程和RDB备份机制很类似,不同的是此处的RDB是按照二进制的格式来组织数据的,而AOF重写,则是按照AOF的要求的文本格式来生成的。
首先仍然还是会有一个父进程,当执行重写操作时,他会复制出一个子进程fork,父进程仍然负责接收请求,而子进程负责对aof文件进行重写~~这里和RDB一样一开始没有进行修改的时候,它是继承fork之前父进程的内存。因此,子进程里的内存数据是父进程fork之前的状态,fork之后,新来的请求,对内存的修改是子进程不知道的~~
此时,父进程这里又准备了一个aof-write-buf缓冲区——专门放fork之后受到的数据,也就是说子进程写新aof文件的同时,父进程仍然在不停地接收客户端的新请求,并且不断地把这些请求写到aof-write-buf缓冲区。同时父进程还会把这些请求产生地AIF数据写入到原有的缓冲区,刷新到原有的AOF文件中。
当子进程这边,把aof数据写完之后,会通过信号通知一下父进程,父进程再把aof-rewite-buf缓冲区的内容也写入到新AOF文件里~~
完成之后,就可以使用新的AOF文件代替旧的AOF文件了。
补充问题:
1、如果在执行bgwriteaof的时候,当前redis已经正在进行aof重写了,会咋样呢??
此时,就不会再次执行aof重写,直接返回了~~
2、如果在执行bgrewriteaof的时候,发现当前redis在rdb文件的快照,会咋样呢?
此时,aof重写操作就会等待,等待rdb快照生成完毕之后,再进行aof重写。
3、rdb对于fork之后的新数据,就直接置之不理了。aof则对于fork之后的新数据,采取了aof-rewrite-buf缓冲区的方式来处理~~那为啥rdb不采取这样的方式呢??
rgb本身的设计理念,就是用来“定期备份”的。只要是定期备份就难以和最新的数据保持一致~~而aof的理念则是实时备份。实时备份不一定就比定期备份更好,还是要看实际场景的~~但是现在系统的资源一般是比较充裕的,aof的开销也不算事~~一般来说,aof的适用场景是更多一些的。
4、父进程fork完毕之后,就已经让子进程写进新的aof文件了,并且随着时间的推移,子进程很快就写完了新的文件,要让新的aof文件代替旧的~~此时父进程还在继续写这个即将消亡的旧aof文件是否还有意义??
不能不写!!需要考虑到极端情况,假设在重写的过程中,重写了一半,服务器挂了,子进程内存里的数据就会丢失,新的aof内容还不完整,所以如果父进程不坚持写旧aof文件,重启就没法保证数据的完整性了~~
混合持久化
刚才我们讲了aof中的重写机制,下面对重写机制进行演示:
首先,创建key,并替换key的数值:
vim进入aof文件:
通过stat命令得到aof文件的inode:
使用bgrewriteof,触发重写机制:
vim进入aof文件:
此时就又出现疑问了:咦,不是aof文件会按照文本的形式组织数据吗?怎么重写一下变成RDB一样的二进制数据了?
我们退出,vim一下rdb文件,看是否一致:
可以看到,两个文件的内容是一摸一样的,这是为啥呢?
AOF本来是按照文本的方式来写入文件的,但是文本的方式写文件,后续加载的成本是比较高的~~
而redis就引入了“混合持久化”的方式~~结合了rdb和aof的特点~~
按照aof的方式,将每个请求/操作,都记录文件,在触发aof重写之后,就会把当前内存的状态按照rdb的二进制格式写入到新的aof文件中。
这个配置也是可以在redis配置文件中可以配置的:![]()
最后我们再stat一下aof文件,确定它已经被替换了:

那当redis上同时存在aof文件和rdb快照的时候,此时以谁为主呢?以aof为主(aof包含的数据是比rdb更全的)!!rdb就直接被忽略了!!

信号
在刚才AOF重写流程的过程,我们提到子进程完成重写后会给父进程发送信号!由于Java提倡多线程的方式进行并发编程,因此,这个概念比较陌生。
信号这个东西,可以是Linux的神经系统。是进程之间的相互作用,也可以视为是进程间通信的一种手段。
信号能表达的信息有限,并非像socket这样的方式可以传输任意数据~~因此,像上述父子进程的场景中,子进程表达“我干完了”这种简单的信息传递,使用的信号也是ok~~
