深入Redis:细谈持久化

Redis的数据是保存在内存中的,内存里面的数据是不持久的,要想做到持久化,必须要把在内存中的数据储存到硬盘上。

Redis速度非常快,数据只有在内存中才有这样的速度,但是为了持久,数据还是要想办法保存到硬盘上去。于是,Redis决定,内存中也存数据,硬盘上也存数据,这样的两份数据理论上是完全一样的。

当需要插入一个新的数据的时候,就把这个数据同时写入到内存和硬盘,当查询某个数据的时候直接从内存里面读取,硬盘里面的数据只是在redis重启的时候,用来恢复内存里面的数据的。

具体来说,Redis有两种持久化的策略:RDB和AOF。

目录

RDB

手动触发

bgsave执行流程

dump.rdb

自动触发

AOF

同步频率

重写机制

重写流程

混合持久化


RDB

RDB,Redis DataBase ,也就是定期备份。

RDB定期的把我们Redis内存中的所有数据,都写入到硬盘中,生成一个快照。后续Redis一旦重启,就可以根据快照来把数据重新恢复到内存中。

“定期”,具体来说,有两种方式:手动触发和自动触发。

手动触发

通过redis客户端,执行特定的命令,来触发快照的生成。

这个特定的命令就是 save  或者  bgsave(backgroud)。

执行save的时候,redis会全力以赴的进行“快照生成”操作,此时会阻塞redis的其他客户端的命令。

但是并不推荐这样的方式,因为会占用大量的资源,会导致类似于 keys * 的后果。

所以我们主要讲解的是bgsave。

bgsave处理的时候,是不会影响到Redis服务器处理其他客户端的请求和命令。是如何做到的呢?Redis不是单线程的吗?此处Redis使用的就是“多线程”的方式来完成的并发编程。

bgsave执行流程

按照 1) 2) 3) 4) 5)的顺序来看,bgsave在后台异步地保存当前数据库的数据到磁盘。这个过程是通过创建一个子进程(fork)来完成的,这样可以避免在数据保存过程中阻塞主进程,从而影响Redis的性能。等到子进程完成生成RDB文件以后,在通过一个信号通知父进程。

dump.rdb

redis生成的rdb文件,是存放在redis的工作目录中的,可以在redis配置文件中设置。

redis.conf文件中,有一个名为dir的配置项,它指定了RDB文件和AOF文件的存储目录。

dir /var/lib/redis

后续redis服务器重新启动,就会尝试加载这个rdb文件。如果这个文件的格式或者某方面有错误,也可能导致数据加载失败。这里具体redis会怎么样,取决于rdb文件坏的地方在哪里,如果坏的地方正好是在文件末尾,就有可能还能正常启动,但是如果中间位置坏了,可能直接启动失败了。

当生成rdb镜像的时候,此时就会要把生成的快照数据,保存到一个临时文件中,当这个快照生成完毕之后,会先删除之前的rdb文件,再把新生成的rdb文件的名字改成刚才的dump.rdb。

自动触发

在Redis配置文件中,可以自己设置Redis每隔多久/修改多少次 就可以触发RDB。

  • save 900 1:如果在900秒(15分钟)内至少有1个键被修改,则保存数据库。
  • save 300 10:如果在300秒(5分钟)内至少有10个键被修改,则保存数据库。
  • save 60 10000:如果在60秒内至少有10000个键被修改,则保存数据库。

假设我们随便插入几个键值对,没有运行手动触发的命令,也达不到自动触发的条件。但是这一些数值都是可以修改的,但是需要注意的是:生成一次RDB快照,成本还是比较高的,不能让这个操作执行的太频繁。

正因为rdb生成的不能太频繁,这就导致快照里面的数据和当前实时的数据情况可能有一些偏差。

  1. 12:00:00  生成了rdb
  2. 12:00:01  redis收到了大量的key变化请求
  3. 12:01:00  生成下一个快照文件

如果在第二步的过程中,redis服务器挂了,或者被异常重启(如kill - 9或者服务器掉电),就会导致12:00:00之后的数据都丢了。解决这个的办法就是AOF,我们之后再说。

如果使用service redis-server restart (shutdown命令),redis服务器也会触发生成快照这一操作。

RDB最大的问题,就是不能实时的持久化保存数据,在两次生成快照之间,实时数据可能会随着服务器重启而丢失。

AOF

AOF,append only file,会把用户的每个操作都记录到文件中,当redis重新启动的时候,就会读取这个aof文件中的内容,用来恢复数据。

AOF一般是关闭状态,通过修改配置文件可以打开。

那redis是一个单线程的服务器,在操作内存的同时,又要实时操作aof,同时写硬盘和内存,速度怎么保证?

实际上,AOF机制并不是直接让工作线程把数据写入硬盘,而是先写数据到内存中的缓冲区,等到有一定量之后再统一写入硬盘。

但是这不就是和RDB一样的问题了吗,如果数据在内存缓冲区里面,还没有写入到硬盘里面就主机掉电等出现异常,那岂不是还是会有部分数据丢失?

同步频率

其实这就是程序员需要做的取舍,redis给出了一些选项,刷新频率越高,性能影响就越大,同时数据的可靠性就越高;刷新频率越低,性能影响就越小,数据的可靠性就越低~

在配置文件中,有一个appendfsync选项可以自行设置。

重写机制

AOF文件持续增长,体积会越来越大,而且会影响到下一次 redis的启动时间。有没有什么办法能够减少AOF的体积呢?在AOF文件中,有一些内容是冗余的,比如:

对于AOF文件来说,我们可以忽略一些操作的过程,只关注操作的结果,这样就能够节省很多的资源占用。这就是Redis的重写机制,能够针对AOF文件进行整理操作,这个整理就能够剔除其中的冗余操作,并且合并一些操作,达到给AOF文件瘦身的效果。

重写流程

对于AOF的重写来说,仍然会创建子进程fork。父进程仍然负责接收请求,子进程负责对AOF文件进行重写。重写的时候,不关心AOF文件中原来都有一些什么,只关心内存中最终的数据状态。子进程只需要把内存中当前的数据获取出来,以AOF的格式写入到一个新的AOF文件中。

因为内存中的数据的状态,就已经相当于把AOF文件整理后的模样了。

  1. 在fork后,子进程写新的AOF文件,父进程仍然在不停的接受客户端的新请求,父进程会把这些请求产生的AOF文件先写到缓冲区里面,再刷新到原来的AOF文件。
  2. 在创建子进程的一瞬间,子进程就继承了当前父进程的内存状态,因此子进程的内存数据是父进程fork之前的状态,fork之后,新来的请求子进程是不知道的,它在自己干自己的事。
  3. 父进程又准备了一个aof_rewrite_buf缓冲区,专门放fork之后收到的数据。等待子进程写完新的AOF文件后,再把缓冲区里面的文件写入到新AOF文件中,就可以用新的AOF文件代替旧的AOF文件了。
  4. 父进程fork完之后,仍然在写旧的AOF文件,并且随着时间的推移新的AOF文件也会完成,那父进程还有必要在fork之后写旧的AOF文件吗?考虑极端情况,fork后子进程重写一半了,但是服务器挂了,子进程的数据就会丢失,新的AOF文件内容还不完整。如果父进程不写旧的AOF文件,重启就无法保证数据的完整性了。

如果在执行bgrewriteaof的时候,此时redis已经在进行AOF重写了,就不会再次执行AOF重写,会直接返回。

如果在执行bgrewriteaof的时候,发现当前redis在生成rdb文件的快照,此时AOF就会等待,等待RDB快照生成完毕再进行AOF重写。

其实AOF这样做的好处就是:RDB对于fork之后的数据就置之不理,而AOF则采用了aof_rewrite_buf来处理。这也就是RDB和AOF的设计理念不同,前者强调定期备份,而后者则是强调实时备份。

混合持久化

AOF本来是按照文本的方式来写入文件的,但是文本的方式写文件,后续加载的成本是比较高的,redis就引入了“混合持久化”的方式,结合了RDB和AOF的特点。

通过这个配置可以打开混合持久化,默认是打开的。

按照AOF的方式,每一个请求、操作都记录到文件里面,在触发了AOF重写操作后,就会把当前的内存按照rdb的二进制格式写入到新的AOF文件中,后续再进行的操作仍然是按照AOF文本的方式追加到文件后面。

当Redis上同时存在AOF文件和RDB快照的时候,以AOF为主,RDB就被忽略了~

至此,Redis持久化就告一段落了~之后还会继续进行Redis的学习~

  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值