(一)认识持久化
我们都知道redis在内存中存储数据的,但是内存中的数据是不持久的,如果想要让数据持久,就需要把数据存储到硬盘上,但是如果直接存到硬盘上,那我们redis就和mysql没有什么区别了,所以为了保证自身快的特点,我们还是要把数据存放到内存中,但是为了持久化硬盘上也要存。
当我们插入一个新的数据时,我们就把数据同时写入到内存和硬盘中,当查询某个数据的时候,就直接从内存读取,当我们redis重启时,我们从硬盘中取数据来恢复内存中的数据
这里我们会有疑问,那存储了两遍会不会导致我们的效率变低?其实会有一点影响,但是问题不大,整体效率还是很高,只是我们要消耗更多的空间
所以redis在内存中和硬盘上都存储了数据,在硬盘和内存中存储的数据理论上是完全一样的,但是在某些时刻可能会有所不同,这个具体看我们怎么进行持久化
(二)实现策略
我们redis实现持久化有两种策略,一个是rdb一个是aof
那rdb就是定期备份,每一定时间和一定次数就备份一次,aof就是实时备份,只要进行了更改就备份一次
1.RDB
rdb会定期把redis内存中的所有数据都写入硬盘中,生成一个‘快照’
简单理解就是redis给内存中当前存储的数据全都存储到硬盘(生成一个文件),后续redis重启,我们就会根据这个快照把内存中的数据给恢复回来
那我们说定期备份来说也有两种方式:
1.手动触发
我们通过redis客户端,执行save或者bgsave命令,来触发快照生成
save:阻塞当前redis服务器,直到我们生成快照的过程完成,对于内存比较大的实例会造成长时间的阻塞,我们基本上不使用
bgsave:redis进程执行fork操作创建子进程,rdb持久化过程由子进程负责了,完成后自动结束(通常不影响redis服务器处理其他客户端的请求和命令)而且注意redis是单线程模式,所以我们这里虽然是通过并发的情况,但是是通过多进程的方式
我们来看一下这个bgsave命令都干了什么,首先他会给我们的父进程传达命令,然后父进程回给我们fork一个子进程(父进程fork的时候会阻塞),这个子进程来生成RDB文件,子进程生成文件的同时,父进程可以正常执行他的命令(此时如果又有一个bgsave,父进程会判断是否有子进程在运行,如果有就会直接返回),然后子进程生成完后会给我们父进程发送一个信号,父进程会替换掉我们现有的rdb文件,并且销毁我们的子进程
注意是rdb文件替换,所以我们同时只会有一个RDB文件
我们子进程完成持久化时,我们数据太少,是观察不到的,但是我们可以观察替换后的文件
我们可以看一下文件的inode(相当于我们的身份证)就是文件的编号
这就是rdb给我们生成的rdb文件(在 /var/lib/redis这个路径中)
那我们打开这个RDB文件看一下
这里我之前是在redis中放了一些数据的,我们主要关注rdb文件的数据,我们发现是二进制数据,而RDB文件也是一个二进制文件,把内存中的数据压缩成二进制并保存到二进制文件中,那这里我们就会有疑问?为什么要存二进制数据,存二进制数据还要压缩,消耗cpu资源??
首先,我们存成二进制数据,最明显的就是我们节省了空间,但是这个并不重要,因为我们的硬件资源是够用的,主要是因为重启redis服务器的时候,我们需要读取硬盘上的RDB文件,来获取到我们存储的数据,而二进制的数据读取的速度是比较快的
我们如果改一下这个RDB文件发生什么呢?
最好的情况就是没什么大事,我们redis服务器可以正常启动(在数据末尾改),但是如果我们在二进制数据中间更改,我们就会导致redis服务器无法启动,因为redis无法恢复之前的数据
我们可以看redis给我们返回的日志文件,看看发生了什么(/var/log/redis在这个路径下)
那redis也给我们提供了检查RDB文件是否正确的工具
2.自动触发
在redis配置文件中,设置一下,让redis每隔多长时间/多少次修改就进行一次生成快照
redis生成rdb文件存放在redis的工作目录中,也可以通过redis配置文件设置
那上面我们说了生成的RDB文件放在哪,接下来我们看一个这个配置文件在哪里
我们打开后就能看到配置文件并且修改了
这里的配置文件我改了一下,那我们看一下这个定期存储的自动触发机制,900s后有一次更新就触发一次,300秒内有10次更新就触发一次,注意这个要同时满足才可以
我们更改配置文件的时候要注意,因为生成RDB快照这个操作设置到了进程的创建销毁,写文件等操作,所以这个成本比较高,我们生成快照是不能太频繁的
这就导致了,我们两次生成快照间是有一段时间间隔的,这段时间内,我们的数据是不安全的,如果redis不正确退出了,比如kill -9 (kill后ubuntu会自动再给我们启动一个redis服务器)就会导致我们的数据丢失,因为我们并没有把他写到硬盘上
那为什么我要说不正确退出呢?因为如果我们是重启redis服务器或者关闭redis服务器,我们会自动生成一个快照
那这里我们来统计一下redis生成快照除了手动操作,还有那些情况可以自动触发:
1)首先就是在配置文件中设置自动触发的条件
2)通过shutdo命令,redis里的一个命令,关闭服务器(我们的service redis-server restart/stop都会触发这个命令)
3)redis进行主从复制的时候,主节点自动生成rdb快照,并且把快照给所有的从服务器(这个后面说到主从复制再说)
RDB优缺点:
1) 首先RDB是一个二进制文件,是某一时刻的快照,非常适合用于备份和全量复制,比如定期每天进行备份,并且把RDB文件复制到远程及其中,用于更安全的备份
2) 生成RDB文件是二进制文件,所以加载的速度是比AOF文件快的(因为我们读取到内存中就是按照字节的格式取出,放到对应对象的,而AOF生成的是文本文件,需要对一系列字符串进行切分)
3) RDB的快照生成之间存在空挡,所以是没有办法做到实时持久化,因为这里的fork创建子进程是重量级操作,成本很高
4) RDB文件是用二进制保存的,而Redis又更新了很多版本,兼容性可能有风险
2.AOF
既然RDB快照生成有空挡,无法实时持久化,那么我们就可以使用AOF
AOF持久化:
以独⽴⽇志的⽅式记录每次写命令,重启时再重新执⾏AOF⽂件中的命令达到恢复数据的⽬的。AOF的主要作⽤是解决了数据持久化的实时性,⽬前已经是Redis持久化的主流⽅式。
注:当我们开启AOF的时候,我们在重启服务器的时候就不读取RDB文件了,而是读取我们的AOF文件(所以我们之前AOF都是关闭的)
那我们怎么开启AOF呢?我们需要取redis配置文件(/var/lib/redis)中,找到这个
那我们来看一下,确实多了一个AOF文件,而之前RDB文件中的数据也没有进行读取
那我们打开一下看看,相比于RDB文件,我们更方便理解了,AOF通过一些特殊符号作为分隔符,对命令的细节做区分
AOF的工作流程
内存缓冲区
我们使用AOF会不会影响性能呢?
答案是没有什么影响,虽然我们AOF同时写入硬盘和内存,但是我们并不是直接写到硬盘里,而是先写到一个内存缓冲区中,累积一些数据,统一一次性写入硬盘
而且AOF是顺序读写,顺序读写还是比较快的(虽然比内存要慢),所以虽然可能会有影响,但是几乎可以忽略不记
那主要保证性能的----内存缓冲区,这不也是内存吗,如果我们出了问题,缓冲区的数据不就又没了? 是这样的,但是已经是权衡下的做法了,就像是mysql的事务的隔离级别一样,如果要保证可靠性,那我们的性能就一定会降低
但是我们也可以通过不同频率刷新缓冲区来尽可能的保证数据不丢失
重写机制
随着命令不断写入AOF,文件会越来越大,虽然我们硬盘资源够用,但是AOF有很多冗余的数据,我们是可以去除的,所以我们Redis就引入了AOF的重写机制压缩文件体积并且通过写命令同步到新的AOF文件。
那什么是冗余的数据呢?这要看我们的数据是如何变化的
其实就相当于我们要去除这些中间的步骤
那之所以我们要重写AOF文件去除冗余数据,本质上是为了降低硬盘空间占用和提高启动Redis的数据恢复速度
AOF重写也分为手动触发和自动触发:
手动触发:使用bgrewriteaof命令
自动触发:仍然是在redis配置文件中更改配置项即可
AOF重写流程
我们根据这个图来看一下重写的流程
首先我们父进程如果已经在执行AOF重写了,那新的重写请求就不会执行。如果父进程正在执行bgsave,重写命令就会延迟到bgsave执行后再执行
然后父进程执行fork创建一个子进程,同时我们父进程的fork要继续响应其他命令,期间所有命令写到AOF缓冲区并且同步到硬盘(保证旧AOF文件正确)
子进程只有fork前的信息,我们fork后响应的命令,还要写到AOF重写缓冲区,当我们子进程生成完新AOF文件后,会发送一个信号通知父进程,然后父进程将重写缓冲区的文件与新AOF文件合并,再替换旧AOF文件
那我们来对比一下这个重写和生成快照的区别,无非就是多了一步,我们fork后有个重写缓冲区来存fork后的命令,等子进程生成完后,顺序写入(这也是AOF和RDB的区别)
这里可能我们会有个疑问,为什么我们还需要向旧AOF文件中写数据呢?有AOF重写缓冲区不就够了?这里我们向旧AOF文件中写是为了防止我们服务器挂了,数据丢失,而重写缓冲区的数据还没来得及写入到新AOF文件中(这样我们无法保证数据的完整性)
但是重写后我们再打开AOF文件,我们会发现变成了二进制文件
这是因为文本文件后续加载成本是比较高的,我们redis就引入了混合持久化的方式,这个方式结合了rdb和aof的一些特点
我们按照aof的方式将每个请求/操作记录文件中,但是触发重写后,我们会把当前内存的状态按照二进制文件放到新生成的aof文件,但是后续的操作我们仍然是使用AOF文本的方式追加到文件后
我们仍然是在redis配置文件改
(所有配置项更改后都要重启redis服务器后才生效)
启动时数据恢复
当redis启动时,会根据RDB或者AOF的文件内容,进行修复,我们上面说,当我们开启了AOF就默认根据AOF文件中的数据进行恢复
因为AOF的数据对比RDB是更全的