zoukankan      html  css  js  c++  java
  • Redis持久化之RDB&AOF

      在说Redis持久化之前,需要搞明白什么是数据库状态,因为持久化就是将内存中的数据库状态保存到磁盘上。那么什么是数据库状态呢?Redis是一个key-value数据库服务器,一般默认有16个数据库,可以使用select <index>命令进行切换(0-15),这每个非空的数据库又可以包含任意多个键值对,为了方便起见,我们将数据库服务器中的非空数据库以及它们的键值对通常为数据库状态,所以持久化,说的不是一个数据库,而是服务器上的所有非空数据库。

      Redis中数据存储模式有2种:cache-only, persistence;

    • cache-only 即只做为“缓存”服务,不持久数据,数据在服务终止后将消失,此模式下也将不存在“数据恢复”的手段,是一种安全性低/效率高/容易扩展的方式;
    • persistence 即为内存中的数据持久备份到磁盘文件,在服务重启后可以恢复,此模式下数据相对安全。

      对于 persistence 持久化存储,Redis 有两种持久化方案,RDB(Redis DataBase)和 AOF(Append-Only File)。其中RDB是一份内存快照,AOF则为可回放的命令日志,他们各有特点也相互独立。4.0开始允许使用RDB-AOF混合持久化的方式,结合了两者的优点,通过 aof-use-rdb-preamble 配置项可以打开混合开关。

    一. RDB详解

      RDB 就是 Snapshot 快照存储,是默认的持久化方式。它按照一定的策略周期性地将数据存储到磁盘,生成名为 dump.rdb 的文件,RDB的执行周期可以通过配置文件中的save来配置。在指定的时间间隔内,执行指定次数的写操作,会将内存中的数据写入到磁盘中。即在指定目录下生成一个 dump.rdb 文件。Redis 重启会通过加载 dump.rdb 文件恢复数据。实际操作过程是 fork 一个子进程,先将数据集写入临时文件,写入成功后,再替换之前的文件,用二进制压缩存储。

      RDB 持久化默认生成的文件名为 dump.rdb ,这个可以通过配置文件配置,RDB文件一个经过压缩的二进制文件,接下来介绍一些RDB文件结构,RDB文件包含五个部分,分别是【Redis |  db_version  |  databases | EOF | check_sum】开头的Redis表示这是一个RDB文件,服务器可以通过这个快速检查载入的文件是否是RDB文件,db_version 是一个整数,代表RDB文件的版本,databases 部分包含0个或者是任意多个数据库,以及数据库中的键值对数据,如果数据库状态是空,那么这部分也是空的,这部分的结构如下【SELECTDB | db_number | key_values_pairs】其中 SELECTDB 是一个常量,长度是一字节,当服务器遇到这个的时候,知道接下来要读入的将是一个数据库号码,db_number中保存的是一个数据库号码(0-15)这两部分结合就可以切换到相应的数据库,然后读取键值对了,键值对的部分的结构有两种,一种是带过期值的,一种是不带过期值的,如果是带过期值,那么结构是【EXPIRETIME_MS | ms |  TYPE | key | value】第一个常量和 SELECTDB 一样,第二部分是毫秒为单位的 UNIX 时间戳,就是键值对的过期时间,然后是 TYPE 记录了 value 的类型,是 String,list,set,zset,hash 等。不带过期值的键值对部分的结构没有前两部分,只是【TYPE | key | value】。

      RDB持久化可以通过命令进行手动触发,也可以配置好后让服务器自动触发,手动触发可以使用Redis命令【SAVE】或者【BGSAVE】,这两个命令有一些差别:save 命令会阻塞服务器进程,save 命令执行的时候服务器不能够处理任何命令请求,直到 save 命令执行完毕,RDB文件创建完毕。bgsave 命令不会阻塞服务器,而是通过派生出一个子进程,然后由子进程负责创建RDB文件,服务器进程继续处理命令请求,这里需要说明一下,bgsave 处理期间,服务器进程虽然能够继续处理命令请求,但是save,bgsave,bgrewriteaof 这三个命令的处理方式会和平时有所不同,在 bgsave 期间,save 命令会被服务器拒绝,服务器禁止 save 和 bgsave 同时执行,避免父进程和子进程同时执行两个rdbSave(创建RDB文件的实际工作实际上是由 rdbSave 函数完成,save 和 bgsave 都会调用这个函数,只是调用的方式不同)调用,bgsave 命令在 bgsave 期间也会被拒绝,理由和拒绝 save 的理由一样,两个 bgsave 也会产生竞争,bgrewriteaof 命令会被延迟到 bgsave 执行完毕之后执行。 

      RDB操作借用 copy on write 机制进行写时复制,父进程 fork 一个子进程,由子进程进行内存遍历将数据写入临时文件,父进程仍处理客户端请求,待子进程执行完毕,将临时文件 rename 为 dump.rdb,因此无论RDB是否成功,dump.rdb 都是完整的。

      bgsave 是主流的触发RDB持久化方式,下图是运作流程:

      Redis内部还存在自动触发RDB的持久化机制,例如一下场景:

        1)使用 save 相关配置,如‘save m n’表示m秒之内数据集存在n次修改时,自动触发 bgsave。

        2)如果从节点执行全量复制操作,主节点自动执行 bgsave 生成RDB文件并发送给从节点。

        3)执行 debug reload 命令重新加载Redis时,也会自动触发save操作。

        4)默认情况下执行 shutdown 命令时,如果没有开启AOF持久化功能则自动执行 bgsave。

      Redis没有专门的RDB文件载入命令,只要Redis服务器开启,就会检测RDB文件是否存在,就会自动载入RDB文件,这里需要说明一点,如果服务器开启了AOF持久化功能,服务器会优先使用AOF文件来还原数据库状态,只有在AOF持久化功能关闭的时候,才会使用RDB文件来还原数据库状态。

      接下来说一下,RDB自动保存,前面已经说了,RDB可以通过手动执行,SAVE命令和BGSAVE命令,也可以通过配置让服务器自动执行,那么如何配置呢?

      Redis.conf中可以配置,默认配置如下:

    save 900 1
    save 300 10
    save 60 10000

      以上表示的意思是:

    • 900秒之内对服务进行了至少一次修改
    • 300秒之内服务器进行了至少10次修改
    • 60秒之内对服务器进行了至少10000次修改。

      这些条件满足其中的任意一个bgsave命令就会自动执行。

      那么服务器是如何知道我做了多少修改的?服务器中有个dirty计数器和一个lastsave时间戳,当服务器执行一个数据库修改命令之后,dirty计数器就会进行更新,命令修改了多少次数据库,dirty就会增加多少,如:【set msg  hello】修改了一个,那么dirty就加一,如果【mset msg word name nihao age 20】那么dirty就增加三。lastsave属性记录上次服务器执行保存操作的时间,是一个unix时间戳,通过这两个属性,可以很简单的距离上次保存已经多少时间了,以及修改了多少次数据库,一旦满足以上三个条件,那么就自动调用bgsave命令,同时更新lastsave属性和dirty属性归零。

      至于检查保存条件是否满足这个工作,是由Redis服务器周期性操作函数serverCron默认间隔100毫秒执行一次检查,这个函数有很多地方用到,注意一下,这个函数是对正在运行的服务器进行维护的函数,在Redis事件中会有提到(Redis服务器是一个事件驱动程序,什么是事件驱动呢?就是发生事件的时候才会动一下,不然就跟死了一样,事件驱动又分为文件事件和时间事件,ServerCron就是一种时间驱动,至于文件驱动,其实就是客户端发过来一个命令,服务器才会去执行,然后给客户端返回结果)

    RDB文件处理

      保存:RDB文件保存在dir配置指定的目录下,文件名通过dbfilename配置指定。可以通过执行config set dir {newDir} 和 config set dbfilename {newFileName}运行期动态执行,当下次运行时RDB文件会保存到新目录。

      压缩:Redis默认采用LZF算法对生成的RDB文件做压缩处理,压缩后的文件远远小于内存大小,默认开启,可以通过参数config set rdbcompression {yes|no}动态修改。

      校验:如果Redis加载损坏的RDB文件时拒绝启动,并打印如下日志:

    Short read or OOM loading DB. Unrecoverable error , aborting now.

      这时可以使用Redis提供的RDB文件检查工具 redis-check-dump 检测RDB文件是否完整并获取对应的错误报告。

    RDB的优缺点

      优点:

        1、RDB是一个紧凑压缩的二进制文件,代表Redis在某一个时间点上的数据快照。非常适合用于备份,全量复制等场景。比如每6小时执行bgsave备份,并把RDB文件拷贝到远程机器或者文件系统中(如hdfs),用于灾难恢复。(一旦采用该方式,那么你的整个Redis数据库将只包含一个文件,这对于文件备份而言是非常完美的。比如,你可能打算每个小时归档一次最近24小时的数据,同时还要每天归档一次最近30天的数据。通过这样的备份策略,一旦系统出现灾难性故障,我们可以非常容易的进行恢复。)

        2、性能最大化。对于Redis的服务进程而言,在开始持久化时,它唯一需要做的只是fork出子进程,之后再由子进程完成这些持久化的工作,这样就可以极大的避免服务进程执行IO操作了。

        3、如果数据集很大,RDB的启动、加载RDB恢复数据的效率远远快于AOF方式,如果业务对数据完整性和一致性要求不高,RDB是很好的选择。

      缺点:

        1 数据的完整性和一致性不高,因为RDB可能在最后一次备份时宕机了。(如果你想保证数据的高可用性,即最大限度的避免数据丢失,那么RDB将不是一个很好的选择。因为系统一旦在定时持久化之前出现宕机现象,此前没有来得及写入磁盘的数据都将丢失。)

        2、备份时占用内存,因为Redis 在备份时会独立创建一个子进程,将数据写入到一个临时文件(此时内存中的数据是原来的两倍),最后再将临时文件替换之前的备份文件.RDB方式数据没办法做到实时持久化/秒级持久化。因为bgsave每次运行都要执行fork操作创建子进程,属于重量级操作,频繁执行成本过高。所以Redis 的持久化和数据的恢复要选择在夜深人静的时候执行是比较合理的。

        3、RDB文件使用特定二进制格式保存,Redis版本演进过程中有多个格式的RDB版本,存在老版本Redis服务无法兼容新版RDB格式的问题。

    RDB文件恢复数据

      将dump.rdb 文件拷贝到redis的安装目录的bin目录下,重启redis服务即可。在实际开发中,一般会考虑到物理机硬盘损坏情况,选择备份dump.rdb 。

    二. AOF详解

      AOF(append only file)持久化:以独立日志的方式记录每次写命令,重启时再重新执行AOF文件中命令达到恢复数据的目的。Redis 默认不开启。AOF的主要作用是为了弥补RDB的不足(数据的不一致性),解决数据持久化的实时性,目前已经是Redis持久化的主流方式。

      AOF 将“操作 + 数据”以格式化指令的方式追加到操作日志文件的尾部,在append操作返回后(已经写入到文件或者即将写入),才进行实际的数据变更,“日志文件”保存了历史所有的操作过程;当server需要数据恢复时,可以直接replay此日志文件,即可还原所有的操作过程。AOF相对可靠,它和MySQL中bin.log、apache.log、zookeeper中txn-log简直异曲同工。AOF文件内容是字符串,非常容易阅读和解析。

    使用AOF

      开启AOF功能需要设置配置:appendonly yes,默认不开启。AOF文件通过appendfilename 配置设置,默认文件名是appendonly.aof。保存路径同RDB持久化方式一致。通过dir配置指定。AOF的工作流程操作:命令写入(append)、文件同步(sync)、文件重写(rewrite)、重启加载(load),工作流程如下:

      流程如下:

      1) 所有的写入命令会追加到aof_buf(缓冲区)中。

        ① AOF命令写入的内容直接是文本协议格式,为什么直接采用文本协议格式?

        理由如下:文本协议具有很好的兼容性;开启AOF后,所有写入命令都包含追加操作,直接采用协议格式,避免二次处理开销;文本协议具有可读性,方便直接修改和处理。

        ② AOF为什么把命令追加到aof_buf中?

        理由如下:Redis使用单线程响应命令,如果每次写AOF文件命令都直接追加到硬盘,那么性能完全取决于当前硬盘负载。县写入缓冲区aof_buf中,还有另一个好处,Redis可以提供多种缓冲区同步硬盘的策略,在性能和安全性方面做出平衡。

      2) AOF缓冲区根据对应的策略向硬盘做同步操作。

        Redis提供了多种AOF缓冲区同步文件策略,由参数appendfsync控制:

        always:同步持久化,每次发生数据变化会立刻写入到磁盘中。性能较差当数据完整性比较好(慢,安全)

        everysec:出厂默认推荐,每秒异步记录一次(默认值)

        no:不同步

      3) 随着AOF文件越来越大,需要定期对AOF文件进行重写,达到压缩的目的。

      随着命令不断写入AOF,文件会越来越大,Redis引入了AOF重写机制压缩文件体积。AOF文件重写是把Redis进程内的数据转化为写命令同步到新AOF文件的过程。

      AOF重写由父进程fork一个子进程,子进程遍历数据库内存(并没有读取旧文件)并将数据记录到临时文件,父进程继续接收客户端请求,将后续写操作追加到appendonly.aof和AOF重写缓存,待子进程执行完毕,将缓存内容追加到临时文件,并rename为appendonly.aof完成重写操作.

      触发机制:当AOF文件大小是上次rewrite后大小的一倍且文件大于64M时触发。这里的“一倍”和“64M” 可以通过配置文件修改。

      重写后的AOF文件为什么可以变下?有如下原因:

        ① 进程内已经超时的数据不再写文件。

        ② 旧的AOF文件含有无效命令,如del key1、 hdel key2、srem keys、set a 111、set a 222等。重写使用进程内数据直接生成,这样新的AOF文件只保留最终数据的写入命令。

        ③ 多条写命令可以合并为一个,如lpush list a、lpush list b、 lpush list c 可以转化为:lpush list a b c。为了防止但挑明了过大造成客户端缓冲区溢出,对于list、set、hash、zset等类型曹组,以64个元素为界拆分为多条。 

        AOF重写降低了文件占用空间,除此之外,另一个目的是:更小的AOF文件可以更快地被Redis加载。

      AOF重写过程可以手动触发和自动触发:

        ① 手动触发:直接调用bgrewriteaof命令;

        ② 自动触发:更具auto-aof-rewrite-min-size(运行AOF重写时文件最小体积,默认为64MB)和auto-aof-rewrite-percentage参数确定自动触发时机

      4) 当Redis服务重启时,可以加载AOF文件进行数据恢复。

    RDB的优缺点

      优点:

        1、该机制可以带来更高的数据安全性,即数据持久性。Redis中提供了3中同步策略,即每秒同步、每修改同步和不同步。事实上,每秒同步也是异步完成的,其效率也是非常高的,所差的是一旦系统出现宕机现象,那么这一秒钟之内修改的数据将会丢失。而每修改同步,我们可以将其视为同步持久化,即每次发生的数据变化都会被立即记录到磁盘中。可以预见,这种方式在效率上是最低的。至于无同步,无需多言,我想大家都能正确的理解它。

        2、由于该机制对日志文件的写入操作采用的是append模式,因此在写入过程中即使出现宕机现象,也不会破坏日志文件中已经存在的内容。然而如果我们本次操作只是写入了一半数据就出现了系统崩溃问题,不用担心,在Redis下一次启动之前,我们可以通过redis-check-aof工具来帮助我们解决数据一致性的问题。

        3、如果日志过大,Redis可以自动启用rewrite机制。即Redis以append模式不断的将修改数据写入到老的磁盘文件中,同时Redis还会创建一个新的文件用于记录此期间有哪些修改命令被执行。因此在进行rewrite切换时可以更好的保证数据安全性。

        4、AOF包含一个格式清晰、易于理解的日志文件用于记录所有的修改操作。事实上,我们也可以通过该文件完成数据的重建。

      缺点:

        1、对于相同数量的数据集而言,AOF文件通常要大于RDB文件。RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。

        2、根据同步策略的不同,AOF在运行效率上往往会慢于RDB。总之,每秒同步策略的效率是比较高的,同步禁用策略的效率和RDB一样高效。

    AOF文件恢复数据

      正常情况下,将appendonly.aof 文件拷贝到redis的安装目录的bin目录下,重启redis服务即可。但在实际开发中,可能因为某些原因导致appendonly.aof 文件格式异常,从而导致数据还原失败,可以通过命令redis-check-aof --fix appendonly.aof 进行修复 。

    总结

      AOF和RDB文件都可以用于服务器重启时的数据恢复。如图所示,表示Redis持久化文件加载流程:

      Redis 默认开启RDB持久化方式,在指定的时间间隔内,执行指定次数的写操作,则将内存中的数据写入到磁盘中。 RDB 持久化适合大规模的数据恢复但它的数据一致性和完整性较差。 Redis 需要手动开启AOF持久化方式,默认是每秒将写操作日志追加到AOF文件中。

      AOF 数据完整性比RDB高,更加安全,可以将数据更加及时的同步到文件中,但是AOF需要较多的磁盘IO开支。记录内容多了,会影响数据恢复的效率;AOF文件尺寸较大,文件内容恢复数度相对较慢。 
      RDB snapshot,安全性较差,它是“正常时期”数据备份以及master-slave数据同步的最佳手段,文件尺寸较小,恢复数度较快。

      Redis 针对 AOF文件大的问题,提供重写的瘦身机制。若只打算用Redis 做缓存,可以关闭持久化。若打算使用Redis 的持久化。建议RDB和AOF都开启。其实RDB更适合做数据的备份,留一后手。AOF出问题了,还有RDB。

      二者选择的标准,就是看系统是愿意牺牲一些性能,换取更高的缓存一致性(AOF),还是愿意写操作频繁的时候,不启用备份来换取更高的性能,待手动运行save的时候,再做备份(RDB)。RDB这个就更有些 eventually consistent的意思了。

      可以通过配置文件来指定它们中的一种,或者同时使用它们(不建议同时使用),或者全部禁用,在架构良好的环境中,master通常使用AOF,slave使用snapshot,主要原因是master需要首先确保数据完整性,它作为数据备份的第一选择;slave提供只读服务(目前slave只能提供读取服务),它的主要目的就是快速响应客户端read请求;但是如果你的redis运行在网络稳定性差/物理环境糟糕情况下,建议你master和slave均采取AOF,这个在master和slave角色切换时,可以减少“人工数据备份”/“人工引导数据恢复”的时间成本;如果你的环境一切非常良好,且服务需要接收密集性的write操作,那么建议master采取snapshot,而slave采用AOF。

    补充:RDB+AOF 恢复方案

      RDB恢复数据不完整,AOF恢复速度慢,为了解决这两大问题,可以使用RDB+AOF的方案。

      RDB+AOF组合方案是指Redis同时开启RDB和AOF选项,以AOF为主记录日志,当日志文件达到阈值触发AOF重写时,不再使用原有的重写机制,而让Redis服务fork一个子进程执行RDB操作,生成一个临时RDB文件,主进程依然接受客户端请求,并将命令写入AOF文件和一个临时AOF文件中,待子进程结束,将新生成的RDB临时文件rename为dump.rdb,而将临时AOF文件rename为appendonlyfile.aof,至此一次RDB+AOF组合的持久化就完成了。持久化生成的RDB和AOF文件都将用来进行数据恢复,恢复策略是首先Redis数据库加载RDB文件,将数据库恢复到最新一次快照时的状态,然后模拟客户端,将AOF文件中的命令执行一遍,使数据库恢复到上次关机或故障时的状态,这样数据库的恢复就完成了。

  • 相关阅读:
    kvm虚拟化介绍(1)
    linux进阶之yum管理
    python基础之面向对象(三))(实战:烤地瓜(SweetPotato))
    python基础之python牛逼的设计模式
    zabbix监控之邮件报警通知
    zabbix监控之概念和安装
    nosql数据库之Redis集群
    nosql数据库之Redis持久化、备份和主从配置
    nosql数据库之Redis概念及基本操作
    python基础之面向对象(二)(封装、继承、多态)
  • 原文地址:https://www.cnblogs.com/jasonZh/p/9544895.html
Copyright © 2011-2022 走看看