初学Redis之Redis持久化之AOF日志与内存快照RDB

学习AOF日志以及内存快照。

修改于2021年1月8日。

一个数据库,想要成为可靠的,那不可缺少的就是持久化
在redis中,想要数据可靠,有很多功能,持久化、主从复制、哨兵集群等。这篇文章主要记录的是持久化。

若Redis服务宕机了,如何避免数据丢失?

如果redis作为缓存数据库,那么宕机后我们首先能想到的是从数据库中恢复数据。这会出现两种问题:

  • 频繁访问数据库,会给数据库造成压力。
  • 数据库读取是慢读取,肯定没有redis速度快,会导致应用程序响应变慢。

那么对redis来说做数据持久化就需要避免频繁的从后端数据库中恢复数据。redis中有两种方式来保证数据的可靠性:

  • AOF(Append Only File),只允许追加写
  • RDB内存快照

AOF日志

我们常见的数据库日志写入就是“写前日志”,在命令执行之前,就将命令写入日志中。然而AOF则是“写后日志”,在命令执行成功后,再将命令写入日志中。

然而为了避免额外的开销,redis执行AOF日志的命令时,不会去检查命令语法是否正确。那么如果先记录日志再执行命令的话,在使用日志恢复数据的时候,就有可能会出错。

“后写日志”就不存在上述的问题,redis在执行命令后写入日志,否则,会给客户端报错。记入日志是在执行命令之后操作的,所以不会阻塞当前的命令操作

AOF日志写入的就是redis的每一条命令语句,我们以set key1 field1 为例(看下图)。查看一下AOF日志的内容,*3 表示当前命令有三个部分,其中每个部分以 $+数字 开头,后面紧跟着的是具体的命令、键或值。数字表示的是命令、键、值的字节长度,比如:$3 set。

在这里插入图片描述
使用了AOF,还是会有几个风险:

  1. 在redis执行命令之后,还来不及记录日志,这时候系统宕机了,还是会有数据丢失的风险。
  2. 虽然写入日志不会造成当前命令的阻塞,但是可能会阻塞后面的操作。因为每个命令之后会写入到磁盘中的日志文件,若磁盘的压力过大,写入的速度就会很慢,就会阻塞后面的操作。

这时,redis就提供了几种写回策略。

写回策略

总共有三种写回策略,分别为:Always、Everysec、No

Always(“马上写回”):在执行命令之后,马上写入日志。这种策略数据可靠性高,但是系统性能低。
Everysec(“读秒写回”):在执行命令之后,把日志放入AOF文件的内存缓冲区中,然后每一秒写入一次日志。这种策略有一秒内数据丢失的风险,系统性能比Always好。
No(“操作系统写回”):在执行命令之后,把日志放入AOF文件的内存缓冲区中,由操作系统决定何时写入日志。这种策略会有大数据丢失的风险,但是系统性能最好。

策略名称优势、缺点
Always同步写回数据可靠,基本不会有数据丢失,但是性能低下
Everysec每秒写回会有少量数据丢失,性能中等
No操作系统写回性能较好,数据不可靠,可能会有大量数据丢失
总结上面:
如果不在意数据的丢失,只在意系统性能,可以选择No;
如果不在意数据的少量丢失,可以选择Everysec;
如果不能容忍数据的丢失,不在意系统性能,可以选择Always;

那么如果AOF文件过大呢?AOF文件中数据一直增加,长此以往AOF文件势必会越来越大,那么到时无论是写入日志还是数据恢复都会很慢,更可能会造成主线程的阻塞。

那么就是接下来要说的,就是AOF重写机制。

AOF重写机制

AOF重写就是根据redis客户端现状,重新生成一个新的AOF文件。也就是获取现有的键值对,然后将每个键值对用一条新的语句命令写入到新AOF文件。

新的AOF文件为何会更小?
那是因为旧AOF文件中若记录了一个key的LPUSH LPUSH LPOP RPOP等多个操作命令,只需要获取到最终该key的值,用一条LPUSH key field 这样的语句就好,就节省了3条语句的内存。

虽然说重写AOF日志内存会缩小,但是获取整个数据库的键值对并将日志写入到硬盘中,仍然是非常耗时的过程。

那么,重写AOF日志会造成阻塞吗?
重写AOF日志是由主进程fork出一个子进程bgrewriteaof来操作。fork这一过程就是操作系统中的“写时复制”,就是为了避免一次性拷贝大量的内存数据造成阻塞时间过长,单fork子进程需要拷贝必要的数据结构其中包括了内存页表(虚拟内存和物理内存的映射索引表)。这个拷贝的过程会消耗大量的CPU资源,在这个过程完成之前是会阻塞主线程的,这个阻塞时间由内存实例大小、内存页表大小决定,内存越大,阻塞越久。若系统设置了内存大页机制(Huge Page),那阻塞的风险就会越大。所以redis系统中,要将内存大页机制(Huge Page)设置取消掉。

所以说,若实例越大,fork时间越长,那么阻塞的时间也会越久。

什么时候会触发AOF重写呢?

  1. 我们可以自己手动触发bgrewriteaof,在子进程中创建一个体量较小的aof文件,当把日志写入这个小体量的aof文件后,就把大体量的aof文件删除。
  2. 也可以进行配置:
    auto-aof-rewrite-percentage = 100
    auto-aof-rewrite-min-size = 64
    -----这两个配置的意思是,当前aof文件的大小大于64M并且比之前重写aof的体量增加100%的时候,进行重写。
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

可以在redis.window.conf中进行配置。

RDB内存快照

RDB内存快照就某一个时刻的内存数据“拍”下来,生成一个rdb文件,其中rdb是指Redis DataBase。这时就算内存宕机了,rdb文件也不会丢失。rbd文件存储的是某一时刻数据,所以只要将在内存中读入快照文件,数据就会很快的恢复。

对那些内存数据做快照?
redis为了保证数据的可靠性,默认使用的是全量快照。因为“拍”的是内存内的全部数据, 生成的rdb文件越大写入到磁盘中的时间就会增加。

每次在处理一个操作的时候,都要思考一下redis的灵魂问题。
内存快照期间,会阻塞主线程吗?

方法效果
save在主线程中执行,会阻塞主线程
bgsave会生成一个子进程,专门用于rdb文件的写入,所以并不会阻塞主线程,同时Redis RDB的生成策略默认使用的就是bgsave。
所以呢,阻塞肯定是会阻塞的,要看fork的实例大不大,什么都没有完美的不是吗?

在快照期间能修改数据吗?
比如我们给朋友拍照,那么我们就要告诉他你不要动,不然照片就糊了。给数据拍照也是同样道理,我们是不允许数据被改动的。
但是如果生成快照发送了20秒,这20秒的时间内,都不允许进行写操作,这肯定是不能允许的。
这时就通过了操作系统的“写时复制”技术,在快照期间,正常工作。
简单来说,bgsave是由主线程fork出来的子进程,子进程与主线程共享内存数据。这时若修改一块区域(键值key),就复制这块区域生成一块副本,bgsave就将副本的数据写入到rdb文件中。
这就让被正在被拍照的数据也能被修改了。

要在何时内内存数据做快照?
这边设置第一次生成快照时间为T0,经过N秒后T0+N时,再次进行快照,若在T0-T0+N期间系统宕机了,那就会丢失N秒内的数据。

但是若N的时间过短,可能会照成第一个快照生成还没有结束,第二次就开始生成了,会照成恶性循环。

这肯定都是不能接受的。

而且频繁的生成rdb文件也会带来两方面的开销:

  1. 频繁全量快照内存数据,就需要频繁的保存磁盘,增加磁盘的压力。
  2. 虽然bgsave是主线程fork出来的子进程,快照期间并不会阻塞主线程,但是在fork的过程还是会阻塞主线程,若频繁的生成快照,就需要频繁的fork,从而导致主线程被频繁的阻塞。

那么如何避免频繁的全量的内存快照?
此时可以使用增量快照,第一次生成快照的时候生成一个全量快照,之后的每次快照都会进行增量。即在全量快照后,需要记住每个被修改的数据,不要小看“记住”这个词,若需要记住,则需要一个新的元数据空间来记住。这就会给内存带来额外的开销,若修改的数据量过大,那么内存开销就会很大, 对于需要宝贵的内存资源的redis来说,就有点得不偿失了。

虽然说rdb文件对比与AOF文件恢复数据的速度快,但是生成快照的时间节点不好把控。两种持久化方法都是有弊有利的。

redis能想不到解决方案吗?这不就在redis4.0后就提出了同时使用内存快照和AOF文件进行数据备份。

即在T0时间进行快照,在T1开始快照之前都使用AOF文件进行日志记录。当T1后生成了快照,这时因为生成的是全量快照所以之后就不需要再使用AOF日志进行恢复了,所以可以清空AOF日志,再记录T1-T2期间的日志,如此往复。

这种方法避免了频繁的fork子进程对主线程的影响,同时AOF日志记录的是两次快照,不需要记录全部日志,就不会出现日志文件过大,不会重写AOF日志,也避免了重复开销。

这种方法,即体验了rdb快速恢复数据的好处,也体验了AOF日志只简单记录命令的优势。谁说“鱼和熊掌不可兼得”?这就可以!

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值