一、前言

  由于疫情的原因,学校还没有开学,这也就让我有了很多的时间。趁着时间比较多,我终于可以开始学习那些之前一直想学的技术了。最近这几天开始学习Redis,买了本《Redis实战》,看到了第四章,前三章都是讲一些Redis的基本使用以及命令,第四章才开始涉及到原理相关的内容。《Redis实战》的第四章涉及到了Redis的持久化、主从复制以及事务等内容,我个人认为这些应该属于Redis中比较重要的部分,也是面试的常考内容。这篇博客就来记录一下Redis的持久化机制。

二、正文

2.1 为什么需要持久化

  学习过Redis的应该都知道,RedisMySQL等关系型数据库不同,它的数据不是存储在硬盘中,而是存放在内存,所以Redis的速度非常快。而这也就会造成一个问题:电脑如果宕机,或者由于某些原因需要重启,此时内存中的数据就会丢失Redis既然把数据存放在内存,自然也就无法避免这个问题。所以,为了在电脑重启后,能够恢复原来的数据,Redis就需要提供持久化的机制,将Redis数据库存储在内存中的数据,在磁盘中进行备份,也就是持久化。而当Redis重启后,去磁盘中将持久化的数据重新读取到内存,便能避免数据的丢失。

2.2 Redis的持久化方式

  Redis提供了两种持久化的方式,分别是:

  • 快照持久化;
  • AOF持久化;

  下面我就来详细地介绍这两种持久化的方式。

2.3 快照持久化(RDB)

  快照持久化也就做RDB持久化。快照持久化的实现方式简单来说就是:Redis将当前内存中存储的数据写入到一个文件中,将这个文件作为Redis当前的一个快照,保存在磁盘中。当Redis重启时,将这个快照文件中存储的内容加载进内存,即可恢复Redis之前的状态。默认情况下,Redis将快照保存在一个叫做dump.rdb的文件中,我们也可以在配置文件中,通过dbfilename选项来设置快照文件的名称;默认情况下快照文件就保存在Redis的安装目录下,我们可以在配置文件中,通过dir选项来配置快照文件的存储路径(其实也是AOF的路径)。

2.4 执行快照持久化的方式

  执行快照持久化有两种方式:

2.4.1 使用配置文件

  第一种方式就是在Redis的配置文件中(windows下这个文件叫redis.windows-service.conf)加上save配置项,比如像下面这样:

save 60 100:在配置文件中加上这一条的意思是,Redis会每60秒检查一次,若在这60秒中,Redis数据库执行了100次以上的写操作,那Redis就会生成一个快照文件,替换原来的快照文件;若不满足这个条件,则不生成快照,继续等待60秒;

  我们可以根据自己的需求,调整每次等待的时间,以及对写操作次数的要求。而且,我们可以在配置文件中,添加多个save选项,比如一个save 60 100,一个save 5 10,则Redis5秒以及每100秒都会判断一次。需要注意的是,我们不应该让生成快照太过频繁,因为这是一个比较消耗资源的工作,会降低Redis的响应速度。

2.4.2 使用指令

  执行快照持久化的第二个方法就是使用Redis指令,Redis提供了两个指令来请求服务器进行快照持久化,这两个指令分别是SAVEBGSAVE

(a)BGSAVE指令

  BGSAVE的执行流程如下:

  1. Redis调用系统的fork(),创建出一个子进程;
  2. 子进程将当前Redis中的数据,写入到一个临时文件中;同时父进程不受影响,继续执行客户端的请求;
  3. 子进程将所有的数据写入到了临时文件后,于是使用这个文件替换原来的快照文件(默认是dump.rdb);

  值得一提的是,通过配置文件执行快照持久化的方式,实际上就是Redis在判断满足条件时,调用BGSAVE指令来实现的。

(b)SAVE指令

  SAVE指令生成快照的方式与BGSAVE不同,Redis执行SAVE指令时,不会创建一个子进程,异步的生成快照文件,而是直接使用Redis当前进程。执行SAVE指令在创建快照的过程中,Redis服务器会阻塞所有的Redis客户端,直到快照生成完毕,并更新到磁盘之后,才会继续执行客户端发来的增删改查的指令。

  当然,这个指令一般很少使用,因为会阻塞客户端,造成停顿。但是实际上,这个指令的执行效率一般比BGSAVE更高,因为不需要创建子进程,而且在这个过程中,其他操作被阻塞,Redis服务器一心一意地生成快照。在《Redis实战》中,作者提到了使用SAVE指令的一个案例:

  当前服务器的Redis数据库中保存了大量数据,使用BGSAVE指令生成快照会非常的耗时 ,而且由于所剩内存不多,甚至无法创建子进程,于是作者编写了一个shell脚本,这个脚本的内容就是让服务器每天凌晨3点,执行SAVE命令,生成快照,这样就不需要创建子进程,而且由于是凌晨三点,用户较少,SAVE的阻塞机制也不会有太大的影响。

2.5 快照持久化的优缺点

(1)优点:

  1. 快照的rdb文件是一个经过压缩的紧凑文件,它保存了Redis在某个时间点上的数据集,这个文件非常适合用来备份。我们可以存储Redis服务器在不同时间点上的rbd文件,比如说一小时存储一次,一个月存储一次,这样就可以在遇到问题或有特殊需求时,将Redis恢复到某一个时间点;

  2. RDB非常适用于灾难恢复(disaster recovery):它只有一个文件,并且内容都非常紧凑,可以(在加密后)将它传送到别的服务器上;

  3. RDB 可以最大化 Redis 的性能:父进程在保存 RDB 文件时唯一要做的就是 fork 出一个子进程,然后这个子进程就会处理接下来的所有保存工作,父进程无须执行任何磁盘I/O操作,所以不会影响父进程处理客户端的请求;

  4. RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。

(2)缺点

  1. 当我们的服务器发生异常,导致停机时,那我们将会丢失最后一次创建快照后,所作的所有写操作,因为这些操作发生在内存中,还没来得及同步到磁盘。比如我们在配置文件中配置每5分钟生成一次快照,那当系统发生故障导致宕机时,我们将会丢失好几分钟内的操作;
  2. 每次执行BGSAVE创建快照,都需要先创建出一个子进程,再由子进程执行后续操作,当内存中数据较大时,创建一个子进程将会非常耗时,造成服务器等待数毫秒,甚至达到一秒,影响用户体验。而且从这一点也可以说明,我们不能通过提高生成快照的频率,来解决第一个缺点;

2.6 AOF持久化

  AOF全称为只追加文件(append-only file),它的实现方式简单来说就是:AOF持久化机制,会将Redis执行的所有写指令,追加到到AOF的末尾,当服务器发生宕机,或者由于某些原因需要重启时,在重启后,读取AOF文件,重新执行其中记录的写操作,以此达到恢复数据的目的。

  AOF持久化机制默认是关闭的,我们可以在配置文件中,配置appendonly yes来开启。同时我们也可以通过配置appendfsync,控制写操作追加到AOF中的频率,它有如下三种选项:

  • alwaysRedis每次执行写指令,都会立即将这个写指令同步到AOF中;使用这个选项时,Redis发生异常,则最多只会丢失一次写操作(也就是在同步的过程中宕机,没同步完成),但是这也会导致Redis的响应速度变慢,因为此选项会造成频繁的IO操作;
  • everysec(默认):每秒同步一次,使用此选项,速度足够快,而且发生宕机时也只会丢失1s内的写操作;
  • no:让操作系统来决定什么时候进行同步,这个选项速度更快,但是不安全,宕机时丢失数据的多少是不确定的;

  推荐(也是默认),使用第二个选项everysec,每秒进行一次同步,因为这个选项兼顾了速度与安全性,而第一个选项太慢,第三个选项无法保证安全性。

2.7 AOF的重写机制

  AOF持久化的执行机制就是,不断地将写指令追加到AOF的末尾,这样就会导致AOF越来越大。为了解决AOF越来越大的问题,Redis实现了一种优化机制——AOF重写。当Redis检查到AOF已经很大时,就会触发重写机制,优化其中的内容,将它优化为能够得到相同结果的最小的指令集合。

  比如说,我们在Redis中使用SET指令创建了一个String类型的数据,最后使用DEL指令将它删除了。如果开启了AOF持久化,那么则AOF中,将会记录这条SETDEL指令。但是,在执行重写的过程中,这个String最后被删除了,那么Redis就不会将这两条指令加入新的AOF中,因为已经被删除的数据,不需要恢复。再比如说,我们使用Redis维护了一个计数器cnt,我们使用了100INCR指令,让cnt自增到了100,而Redis重写AOF时,可以将这100incr修改为一条SET指令,直接将cnt设置为100,而不是保留100INCR

  经过了重写后,AOF的大小将会大大减小,而且也去除了不必要的操作,优化了恢复数据的指令集。而AOF重写的过程与生成一个快照文件类似,如下:

  1. Redis执行fock(),创建一个子进程用来执行后续操作,而父进程继续处理发送到Redis的执行请求;
  2. 子进程重写旧AOF文件,将重写后的内容写入到一个临时文件;
  3. 如果这个过程中,有新的写指令到达,那么Redis会将这些写指令依旧追加到旧的AOF中,同时也会将这些指令加入到内存的一个缓冲区中。这样做的目的是,如果服务器发生异常,AOF重写失败,这些指令依然能够保存在旧AOF,不会丢失;
  4. 当子进程完成重写工作时,它给父进程发送一个信号,父进程在接收到信号之后,将内存缓存中的所有写指令追加到新 AOF 文件的末尾;
  5. 使用新AOF替换旧的AOF,这之后执行的所有写指令都将追加到新的AOF中;

2.8 AOF的错误处理

  AOF文件是有可能发生错误的,比如上面提过,如果当前Redis正在向AOF中追加一个写指令,但是此时服务器宕机,那么这个存入AOF中的这个写指令就是不完整的,也就是AOF出现了错误。如果停机造成了 AOF 文件出错(corrupt), 那么 Redis 在重启时会拒绝载入这个 AOF 文件, 从而确保数据的一致性不会被破坏。

  那么,当AOF发生了错误,应该如何处理呢?我们可以使用如下命令:

redis-check-aof --fix:redis-check-aof用来检测AOF是否存在错误,如果指定了--fix参数,那么Redis在检测到AOF的错误后,会对AOF进行修复。

  redis-check-aof修复AOF的方式非常简单:扫描AOF文件,寻找其中不正确或不完整的指令,当发现第一个出错的指令后,就将这个指令以及这之后的所有指令删除。为什么需要删除出错指令之后的所有指令呢?因为当一条指令出错,很有可能影响到后续的操作,导致后续操作的都是脏数据,而Redis无法检测哪些指令是受到影响的,所以为了保险起见,就将后续指令全部删除。不过不用担心,因为在大多数情况下,出错的都是AOF最末尾的指令。

2.9 AOF的优缺点

(1)优点:

  1. 使用 AOF 持久化会让 Redis 变得非常耐久,意思就是说,当发生异常导致需要重启服务器时,只会丢失很少的一部分数据,因为AOF持久化默认1s同步一次,也就是说,Redis最多只会丢失1s中所做的修改;
  2. Redis 可以在 AOF 文件体积变得过大时,自动地在后台对 AOF 进行重写: 重写后的新 AOF 文件包含了恢复当前数据集所需的最小命令集合。
  3. AOF 文件有序地保存了对数据库执行的所有写入操作, 这些写入操作以 Redis 协议的格式保存, 因此 AOF 文件的内容非常容易被人读懂, 对文件进行分析(parse)也很轻松。
  4. AOF 文件是一个只进行追加操作的日志文件(append only log), 因此对 AOF 文件的写入不需要进行 seek , 即使日志因为某些原因而包含了未写入完整的命令(比如写入时磁盘已满,写入中途停机,等等), redis-check-aof 工具也可以轻易地修复这种问题。

(2)缺点:

  1. 对于相同的数据集来说,AOF 文件的体积通常要大于快照RDB文件的体积大,因为RDB只存储数据,而AOF中的写指令,既包含指令,也包含数据;
  2. 如果AOF体积太大,那么恢复数据将要花费较长的时间,因为需要重做指令;

2.10 选择快照还是AOF?

  说到这里,可能就有人要问了,在实际生产中,我们应该使用快照持久化还是AOF持久化呢?

1、如果希望自己的数据库有很高的安全性,则应该两者同时使用,AOF用作精确记录,而快照用作数据备份,前面也说过,快照非常适合用来做数据备份,因为它只存储数据库中的数据,并且经过了压缩,而且它恢复Redis数据的速度一般要快于AOF

2、如果可以容忍一段时间内的数据丢失,则可以考虑只使用快照持久化,减小服务器的开销;

  值得一提的是,如果我们同时开启了快照持久化和AOF持久化,Redis在重启后,会优先选择AOF来恢复数据,因为一般情况下,AOF能够更加完整地恢复数据。

三、总结

  快照持久化和AOF持久化各有优劣,在实际生产环境中,我们一般是两者配合使用,快照持久化消耗较低,而且适合用于备份,但是会丢失一段时间的数据;AOF持久化更加地耐久,可靠性更高,但是开销可能相对较高。以上就对Redis的持久化机制做了一个比较详细的介绍,相信看完只后,对Redis会有一个更加深入的理解。

四、参考

详细分析Redis的持久化操作——RDB与AOF的更多相关文章

  1. Redis:持久化之RDB和AOF

    Redis:持久化之RDB和AOF RDB(Redis DataBase) 在指定的时间间隔内将内存中的数据集快照写入硬盘 也就是行话讲的Snapshot快照,它恢复时是将快照文件直接读到内存里. R ...

  2. 分布式缓存Redis的持久化方式RDB和AOF

    一.前言 Redis支持两种方式的持久化,RDB和AOF.RDB会根据指定的规则“定时”将内存中的数据存储到硬盘上,AOF会在每次执行命令后将命令本身记录下来.两种持久化方式可以单独使用其中一种,但更 ...

  3. Redis持久化操作RDB和AOF 对比于HDFS的SecondaryNode

    写在前面的话 最近学习比较多流行的大数据框架和完成两个大数据项目后,又突然学起了Redis.之所以之前的框架不学习记录呢,是因为之前的学习都是为了完成参加服创比赛的项目所以时间较紧,现在基本架构和编码 ...

  4. Redis系列(三):Redis的持久化机制(RDB、AOF)

    本篇博客是Redis系列的第3篇,主要讲解下Redis的2种持久化机制:RDB和AOF. 本系列的前2篇可以点击以下链接查看: Redis系列(一):Redis简介及环境安装. Redis系列(二): ...

  5. redis的持久化方式RDB和AOF的区别

    1.前言 最近在项目中使用到Redis做缓存,方便多个业务进程之间共享数据.由于Redis的数据都存放在内存中,如果没有配置持久化,redis重启后数据就全丢失了,于是需要开启redis的持久化功能, ...

  6. 11、Redis的持久化(RDB、AOF)

    写在前面的话:读书破万卷,编码如有神 --------------------------------------------------------------------------------- ...

  7. redis的持久化(RDB与AOF)

    1.为什么redis要实现持久化? 避免因宕机.断电等场景导致进程退出后数据丢失,如果redis的数据都只存放于内存,那么进程退出后数据就丢失了.持久化机制可以持久化内存数据到硬盘,重启redis后基 ...

  8. Redis数据持久化(RDB、AOF)

    1. 简介   Redis作为内存型数据库,数据都保存在内存中,如果重启或意外宕机后,数据会全部丢失.因此,Redis提供了完善的持久化机制,将内存中的数据持久化到磁盘上,避免了完整性和安全性的问题, ...

  9. Redis 之持久化(rdb、aof)

    Redis的持久化有2种方式   1快照  2是日志 测试aof:

随机推荐

  1. nginx IF 指令

    变量名可以使用"="或"!="运算符 ~ 符号表示区分大小写字母的匹配 "~*"符号表示不区分大小写字母的匹配 "!"和 ...

  2. javax.el.PropertyNotFoundException: 类型[cn.cqsw.pojo.Course]上找不到属性[CourseId]

    今天在JSP利用EL表达式取值报了 "javax.el.PropertyNotFoundException” 1 Caused by: org.apache.jasper.JasperExc ...

  3. js之for与forEach循环的区别

    回武汉打卡第四天,武汉加油,逆战必胜!今天咱们探讨一下for循环和forEach()循环的区别. 首先,for循环在最开始执行循环的时候,会建立一个循环变量i,之后每次循环都是操作这个变量,也就是说它 ...

  4. 用全站 CDN 部署 Discourse 论坛

    Discourse 介绍 Discourse 是一款由 Stack Overflow 的联合创始人--Jeff Atwood,基于 Ruby on Rails 开发的开源论坛.相较于传统论坛,Disc ...

  5. [一、Jmeter5安装及环境配置]

    前言:Jmeter基于Jave底层开发,需要配置Java运行时环境 第一步:首先从Jmeter的官网下载Jmeter,Oracle官网下载Jave; Apache JMeter 5.2.1(需要Jav ...

  6. 第一次将本地项目push到github

    问题:github有一个空项目,将本地项目上传到github空项目时,报错如下 $ git push --set-upstream git@github.com:dslu7733/promise.gi ...

  7. HTML+CSS:css定位详解之相对定位、绝对定位和固定定位

    相对定位 如果想为元素设置层模型中的相对定位,需要设置position:relative;,它还是会占用该元素在文档中初始的页面空间,通过left.right.top.bottom属性确定元素在正常文 ...

  8. html 中video标签视频不自动播放的问题

    有个需求,客户想做个打开官网自动播放一段视频,楼主使用了video标签,即下面的代码::于是我在video标签上添加了属性 autoplay=“autoplay” loop=“loop”然而通过地址栏 ...

  9. 1006 Sign In and Sign Out (25 分)

    At the beginning of every day, the first person who signs in the computer room will unlock the door, ...

  10. Linux 定时实行一次任务命令

    当我们想在指定的时间自动执行 一次 任务的时候,可以使用at命令 启动服务 使用时首先检查atq的服务是否启动 service atd status # 检查atd的状态 service atd st ...