原创

Redis学习笔记(五)——redis持久化详解


一、什么是Redisd持久化

  • Redis相对于Memcache 等其他缓存产品,是当下使用最广泛的缓存产品,不仅仅因为它支持多种数据,存取速度极快,还有另外一大优势——持久化
  • 众所周知,Redis是一个内存数据库,相对于传统的数据库(MySQL、Oracle等),它完全在内存中存取数据,速度明显远远大于硬盘的存取速度。
  • 虽然存取速度快,但是由于数据都在内存中,一旦断电或者服务器宕机,就会导致数据丢失,为此Redis提供将数据持久化到硬盘中,这就是Redis的持久化。

二、Redis持久化分类

为了解决内存数据无法长久保存的缺点,Redis提供了两种持久化的方式,接下来将详细介绍这两种持久化方式。

1. RDB(全量模式的持久化)

1.1 RDB简介

  • 指的就是把当前内存的数据集快照所有键值对数据)写入磁盘,恢复时是将该快照文件直接读取到内存中。
  • Redis中存储的键值对可以理解为Redis的一个状态,当键值对发生变化时(比如写操作等),Redis就会从某一状态切换到另一个状态;RDB就指是在某个时刻,将Redis的所有数据持久化到硬盘中,形成一个快照;当Redis重启时,通过加载最近的一个快照数据,可以将Redis恢复至最近一次持久化状态上。
  • RDB是Redis默认的持久化方式

1.2 配置

  • 详细配置在redis配置文件中的SNAPSHOTTING 区块下,具体大家可以参考之前发布的文章:Redis学习笔记(四)——redis配置文件详解

  • 如果只想使用redis的缓存功能,而不需要数据的持久化,可以修改配置文件中的save配置为空字符串

    #save 900 1 
    #save 300 10
    #save 60 10000 
    save ""
    

    也可以通过命令暂时修改

    redis-cli config set save ""
    

1.3 写入数据

  • 触发方式

    • 自动触发:

      • redis.conf文件中

        save 900 1 # 15分钟内至少有一个键被更改 
        save 300 10 # 5分钟内至少有10个键被更改
        save 60 10000 # 1分钟内至少有10000个键被更改
        

        满足上面三个条件中其中一个即触发持久化操作

      • 默认的rdb文件路径是当前目录,文件名是:dump.rdb,可以在配置文件中修改路径和文件名

        #默认的rdb文件名
        dbfilename dump.rdb
        #默认的rdb文件路径
        dir ./
        
    • 手动触发:savebgsave命令

      • save

        • 可以客户端显示触发,也可以在redis shutdown时触发。Save本身是单线程串行化的方式执行的,即该命令会阻塞当前Redis服务器,执行期间Redis不能处理其他命令,知道持久化结束。

        • 当数据量大时,有肯能会发生Redis Server的长时间卡顿

      • bgsave

        • 可以由客户端显示触发、可以通过配置定时任务触发、也可以在master-slave的分布式结构下由slave节点触发

        • bgsave命令在执行的时候,会fork一个子进程。子进程提交完成之后,会立即给客户端返回响应,备份操作在后台异步执行,在此期间不会影响Redis的正常响应。

        • 注意:由于Redis使用fork来复制一份当前进程,那么子进程就会占有和主进程一样的内存资源,比如说主进程8G内存,那么在备份的时候,必须保证有16G的内存。当内存开销高到使用虚拟内存时,bgsave的Fork子进程会阻塞运行,可能会造成秒级的不可用

        • 基本上 Redis 内部所有的RDB操作都是采用 bgsave 命令

    • savebgsave区别

      命令savebgsave
      IO类型同步异步
      是否阻塞阻塞非阻塞(在fork时阻塞)
      复杂的O(n)O(n)
      优点不会消耗内存不阻塞客户端命令
      缺点阻塞客户命令需要Fork子进程,内存开销大

1.4 恢复数据

将rdb文件移到到启动目录下,redis在启动时会自动加载数据至内存中。在载入rdb文件期间会一直处于阻塞状态,直到载入工作完成为止。

1.5 原理

  • Redis服务器状态数据结构

    struct redisService{
         //1、记录保存save条件的数组
         struct saveparam *saveparams;
         //2、修改计数器
         long long dirty;
         //3、上一次执行保存的时间戳
         time_t lastsave;
    
    }
    
    • saveparams:记录保存save条件的数组

      • 内部元素saveparam的数据结构

        struct saveparam{
             //秒数
             time_t seconds;
             //修改数
             int changes;
        };
        
      • 之前在redis.conf配置文件中对save的配置

        save 900 1
        save 300 10
        save 60 10000
        

        则saveparams数组就会保存三个saveparam元素

        saveparamssaveparams[0]saveparams[1]saveparams[2]
        seconds(秒数)90030060
        changes(修改数)11010000
    • dirty:计数器

      • 记录距离上一次成功执行 save 命令或者 bgsave 命令之后,Redis服务器进行了多少次修改(包括写入、删除、更新等操作)。
    • lastsave:上一次执行保存的时间戳

      • 记录上一次成功执行 save 命令或者 bgsave 命令的时间戳
  • 自动保存原理

    当服务器成功执行一次更新操作,dirty计数器就会加1,而lastsave记录记录上一次成功执行 save 命令或者 bgsave 命令的时间戳。Redis服务器有一个周期性操作函数severCron,默认每隔100毫秒执行一次,该函数会遍历saveparams数组并检查所有保存条件,只要满足一个条件,就会执行bgsave命令。执行完成后,dirty计数器会重置为0,lastsave 也更新为执行命令的完成时间戳

1.6 优缺点

  • 优点

    • rdb文件保存了redis在某个时间点上的所有数据集,非常适合用于数据备份和灾难恢复
    • 生成rdb文件时,redis主进程提供fork一个子进程来处理所有保存操作,主进程基本无影响
    • RDB在恢复大数据集的时候比AOF的恢复速度要快
  • 缺点

    • RDB无法做到实时持久化或秒级持久化,因为bgsave每次运行都需要fork一个子进程,属于重量级操作(内存中的数据被克隆了一份,即内存使用增大一倍),频繁执行成本过高(影响性能)
    • RDB是在一定间隔时间做一次备份,如果中途redis意外宕机,那么就会丢失该间隔时间内的所有更新的数据
    • RDB文件使用特定二进制格式保存,Redis版本演进过程中有多个格式的RDB版本,存在老版本Redis服务无法兼容新版RDB格式的问题(版本不兼容)

2. AOF(增量模式的持久化)

RDB方式虽然简单,但是容易丢失一段时间的数据。如果对数据的完整性有很高的要求,Redis提供了AOF的持久化方式

2.1 AOF简介

  • AOF(append-only-file):通过保存Redis服务器所执行的写命令来记录数据库状态

2.2 配置

  • 详细配置在redis配置文件中的APPEND ONLY MODE区块下,具体大家可以参考之前发布的文章:Redis学习笔记(四)——redis配置文件详解

  • Redis默认是关闭AOF的,如果想要开启,只需将配置文件中appendonly属性修改为yes即可

  • 如果appendonly.aof文件损坏,可以通过redis-check-aof命令来修复

    redis-check-aof --fix appendonly.aof
    

2.3 写入流程

  • Redis的AOF 支持3 种同步策略

    • everysec(默认):每秒异步的触发同步操作

    • always:每一次的刷新缓冲区,都会同步触发同步操作。因为每次的写操作都会触发同步,所以该策略会降低Redis的吞吐量,但是这种模式会拥有最高的容错能力

    • no:由操作系统决定何时同步,这种方式Redis无法决定何时落地,因此不可控

  • 优缺点

    同步策略优点缺点
    everysec每秒1次fsync,IO开销相比always小可能会丢1秒数据
    always不丢失数据IO开销大,一般的STAT盘只有几百TPS
    no无需设置不可控

2.4 回放流程

  • Redis重启时,如果发现存在aof文件,Redis会选择加载aof文件(因为增量的持久化持续的写入磁盘,相比全量持久化,数据更加完整);

  • 回放的过程就是将AOF中存放的命令,重新执行一遍,完成之后再继续接受客户端的新命令。

2.5 AOF的优化重写

  • 由于AOF持久化是Redis不断将写命令记录到 AOF 文件中,随着Redis不断的进行,AOF 的文件会越来越大,文件越大,占用服务器内存越大以及 AOF 恢复要求时间越长。

  • 为了解决这个问题,Redis新增了重写机制,当AOF文件的大小超过所设定的阈值时,Redis就会启动AOF文件的内容压缩只保留可以恢复数据的最小指令集

  • 重写触发机制

    • 可以主动触发

      #执行重写aof操作
      bgrewriteaof 
      
    • 也可以配置文件配置

      #当前AOF文件大小是上次日志重写得到AOF文件大小的二倍(默认设置为100,单位%)时,自动启动新的日志重写过程,
      auto-aof-rewrite-percentage 100
      #设置允许重写的最小aof文件大小,避免了达到约定百分比但尺寸仍然很小的情况还要重写,默认64mb
      auto-aof-rewrite-min-size 64mb
      
  • 重写过程的数据一致性问题

    • 由于重写AOF可能会耗费较长时间,可能影响主进程的操作,所以Redis将AOF重写程序放在了子进程中执行,这样有两个好处:
      • 1)子进程进行 AOF 重写期间,服务器进程(父进程)可以继续处理其他命令
      • 2)子进程带有父进程的数据副本,使用子进程而不是线程,可以在避免使用锁的情况下,保证数据的安全性
    • 但是以上的处理会产生一个新问题:因为子进程在进行 AOF 重写期间,服务器进程依然在处理其它命令,这新的命令有可能也对数据库进行了修改操作,使得当前数据库状态和重写后的 AOF 文件状态不一致。
    • 为了解决这个问题,Redis 服务器设置了一个 AOF 重写缓冲区,这个缓冲区是在创建子进程后开始使用,当Redis服务器执行一个写命令之后,就会将这个写命令也发送到 AOF 重写缓冲区。当子进程完成 AOF 重写之后,就会给父进程发送一个信号,父进程接收此信号后,就会调用函数将 AOF 重写缓冲区的内容都写到新的 AOF 文件中。
    • 通过上面的种种措施,Redis将AOF重写造成的数据不一致问题的影响降到了最低。

2.6 优缺点

  • 优点
    • 相对于RDB来说,AOF方式通过合理的配置最多也就丢失1s数据,数据可靠性大大提高
    • aof文件使用Redis命令追击的方式在构造,便于修正AOF文件
    • aof文件可读性强。例如,如果我们不小心错用了 FLUSHALL 命令,在重写还没进行时,我们可以手工将最后的 FLUSHALL 命令去掉,然后再使用 AOF 来恢复数据
  • 缺点
    • 相同数据下,AOF文件占用内存往往比RDB文件大
    • 由于AOF默认情况下每秒同步一次,所以在高负载情况下,性能不如RDB方式
    • 官方文档指出的AOF 存在隐藏的 bug

三、如何选择合适的持久化方式

  • 如果你仅仅只做缓存,不需要任何持久化,那就当我没写过这篇文章吧(哈哈哈哈哈).....
  • 如果可以忍受一小段时间内数据的丢失,毫无疑问使用 RDB 是最好的,定时生成 RDB 快照(snapshot)非常便于进行数据库备份, 并且 RDB 恢复数据集的速度也要比 AOF 恢复的速度要快,而且使用 RDB 还可以避免 AOF 一些隐藏的 bug;否则就使用 AOF 重写
  • 在实际情况下,不建议使用单一的持久化方式,而且两者同时使用,并且官方也提出未来可能会将 AOF 和 RDB 整合成单个持久化模型
  • 后续文章后介绍一种更为厉害的方式——主从复制模式,敬请期待。

参考

Redis
NoSQL
  • 作者:贤子磊
  • 发表时间:2020-02-23 08:07
  • 版权声明:自由转载-非商用-非衍生-保持署名
  • 评论

    您需要登录后才可以评论