0%

Redis持久化

C是一种通用的编程语言,广泛用于系统软件与应用软件的开发。于1969年至1973年间,为了移植与开发UNIX操作系统,由丹尼斯·里奇与肯·汤普逊,以B语言为基础,在贝尔实验室设计、开发出来。目前C语言编译器普遍存在于各种不同的操作系统中,例如Microsoft Windows、macOS、Linux、Unix等。

一、RDB

  1. RDB持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘,实际操作过程是fork一个子进程,先将数据集写入临时文件,写入成功后再替换之前的文件,用二进制压缩存储。

    • 默认的持久化方式,这种方式是就是将内存中数据以快照的方式写入到二进制文件中,默认的文件名为dump.rdb。
    • 可以配置redis在n秒内如果超过m个key被修改就自动做快照:
    1
    2
    3
    save 900 1     #900秒内如果超过1个key被修改,则发起快照保存
    save 300 10 #300秒内容如超过10个key被修改,则发起快照保存
    save 60 10000 #60秒内容如超过10k个key被修改,则发起快照保存
  2. RDB文件保存过程

    • redis调用fork创建子进程
    • 父进程继续处理client请求,子进程负责将内存内容写入到临时文件。
      • 由于操作系统(os)的写时复制机制(copy on write),父子进程会共享相同的物理页面,当父进程处理写请求时os会为父进程要修改的页面创建副本,而不是写共享的页面,所以子进程的地址空间内的数据是fork时刻整个数据库的一个快照。
    • 当子进程将快照写入临时文件完毕后,用临时文件替换原来的快照文件,然后子进程退出。

client也可以使用save或者bgsave命令通知redis做一次快照持久化。save操作是在主线程中保存快照的,由于redis是用一个主线程来处理所有client的请求,这种方式会阻塞所有client请求

每次快照持久化都是将内存数据完整写入到磁盘一次,并不是增量的只同步脏数据。如果数据量大的话,而且写操作比较多,必然会引起大量的磁盘io操作,可能会严重影响性能。

  1. 优点

    • RDB文件是紧凑的二进制文件,比较适合做冷备,全量复制的场景。
      • RDB做会生成多个文件,每个文件都代表了某一个时刻的Redis完整的数据快照,这种方式非常适合做冷备。因为大量的一个个的文件,可以每隔一定的时间复制出来,将这种完整的数据文件发送到一些远程的云服务、分布式存储上进行安全的存储,以预定好的备份策略来定期备份Redis中的数据。AOF也可以做冷备,只有一个文件,但是你可以写个脚本,每隔一定时间去copy一份这个文件出来,相对比较麻烦。
        • 问题:RDB做冷备,优势在哪儿呢?
          • RDB由Redis去控制固定时长生成快照文件的事情,比较方便,在最坏的情况下提供数据恢复的时候速度比AOF快
          • AOF还需要自己写一些脚本去做这个事情,各种定时
    • RDB持久化速度快
      • RDB每次写都是直接写Redis内存,只是在一定的时候才会将数据写入磁盘中;AOF每次都是要写文件的,虽然可以快速写入os cache中,但是还是有一定的时间开销的,速度肯定比RDB略慢一些
    • RDB数据恢复速度快
      • RDB就是一份数据文件,恢复的时候直接加载到内存中即可;AOF存放的指令日志,做数据恢复的时候要回放和执行所有的指令日志来恢复出来内存中的所有数据的
    • RDB持久化能保持高性能
      *RDB对Redis对外提供的读写服务影响非常小,因为Redis主进程只需要fork一个子进程,让子进程执行磁盘IO操作来进行RDB持久化即可
  2. 缺点

    • 容易数据丢失
      • RDB持久化有个时间间隔,在此期间一旦Redis进程宕机那么会丢失数据
    • fork子进程,如果数据文件特别大会影响服务
      • 一般不要让RDB的间隔太长,否则每次生成的RDB文件太大了,对Redis本身的性能可能会有影响的;
    • RDB无法实现实时或者秒级持久化
      • RDB是间隔一段时间进行持久化,如果持久化之间Redis发生故障,会发生数据丢失。

    二、AOF

    1. AOF持久化以日志的形式记录服务器所处理的每一个写操作,读操作不会记录,以文本的方式记录,可以打开文件看到详细的操作记录。
    • 在Redis的配置文件中存在三种同步方式,它们分别是:
    1
    2
    3
    appendfsync always     #每次有数据修改发生时都会写入AOF文件。
    appendfsync everysec #每秒钟同步一次,该策略为AOF的缺省策略。
    appendfsync no #从不同步。高效但是数据不会被持久化。
  3. AOF文件保存过程

    • redis会将每一个收到的写命令都通过write函数追加到文件中(默认是appendonly.aof)。
    • 当redis重启时会通过重新执行文件中保存的写命令来在内存中重建整个数据库的内容。
  4. AOF文件重写

    • 为什么要重写?重写可以去除数据的中间执行过程,直接保留最终数据命令。

    举个栗子:

    1
    2
    3
    4
    set count 1  // 初始值1 
    incr count // 加1
    incr count // 加1
    decr count // 减1

这个时候获取count的值:

1
2
get count 
“2”

如果不进行AOF重写的话,进行AOF文件恢复的时候,Redis会执行AOF文件中的每一条命令,并最终得到count为2 。

进行AOF重写后,相当于把中间的计算过程略去,直接把计算得到的结果设置进redis,相当于仅执行了一条命令set count 2

  • 可以使用BGREWRITEAOF命令来重写AOF文件。

  • 重写策略

    • auto-aof-rewrite-percentage 100,当前的AOF文件大小超过上一次重写时的AOF文件大小的百分之多少时,会再次进行重写,如果之前没有重写过,则以启动时的AOF文件大小为依据。
    • auto-aof-rewrite-min-size 64mb,限制了允许重写的最小AOF文件大小,通常在AOF文件很小的时候,即使其中有些冗余的命令也是可以忽略的。
  1. 优点

    • AOF可以更好的保护数据不丢失

      • 默认会每隔1秒通过一个后台线程执行一次fsync操作,最多丢失1秒钟的数据
    • AOF日志文件以append-only模式写入,写入性能比较高

      • AOF日志文件以append-only模式写入,所以没有任何磁盘寻址的开销,写入性能非常高,而且文件不容易破损(即使文件尾部破损也很容易修)
    • AOF日志文件即使过大的时候,出现后台重写操作不影响客户端的读写

      • 后台重写的时候,会对其中的指令进行压缩,创建出一份需要恢复数据的最小日志出来。在创建新日志文件的时候,老的日志文件还是照常写入。当新的merge后的日志文件ready的时候,再交换新老日志文件即可。
    • 适合做灾难性的误删除紧急恢复

      • AOF以文件追加的方式进行持久化,非常适合做灾难性的误删除的紧急恢复。如某人不小心用flushall命令清空了所有数据,只要这个时候后台rewrite还没有发生,那么就可以立即拷贝AOF文件,将最后一条flushall命令给删了,然后再将该AOF文件放回去,就可以通过恢复机制自动恢复所有数据
  2. 缺点

    • 对于同一份数据来说,AOF日志文件通常比RDB数据快照文件更大,恢复速度慢;
      • 我们可以简单的认为AOF就是日志文件,此文件只会记录“变更操作”(例如:set/del等),如果server中持续的大量变更操作,将会导致AOF文件非常的庞大,意味着server失效后,数据恢复的过程将会很长。事实上,一条数据经过多次变更,将会产生多条AOF记录,其实只要保存当前的状态,历史的操作记录是可以抛弃的,因此AOF持久化模式加入了重写特性
    • AOF开启后,支持的写QPS会比RDB支持的写QPS低
      • AOF默认每秒fsync一次日志文件,即appendfsync everysec,性能还是很高的;若要一条数据也不丢失则改为appendfsync always,此时Redis的QPS将大幅大降
    • 以前AOF发生过bug,就是通过AOF记录的日志,进行数据恢复的时候,没有恢复一模一样的数据出来
      • 所以说,类似AOF这种较为复杂的基于命令日志/merge/回放的方式,比基于RDB每次持久化一份完整的数据快照文件的方式,更加脆弱一些,容易有bug。不过AOF就是为了避免rewrite过程导致的bug,因此每次rewrite并不是基于旧的指令日志进行merge的,而是基于当时内存中的数据进行指令的重新构建,这样健壮性会好很多。

三、对比

  1. rdb

    • RDB持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘,实际操作过程是fork一个子进程,先将数据集写入临时文件,写入成功后,再替换之前的文件,用二进制压缩存储。
    • RDB是一种表示某个即时点的Redis数据的紧凑文件,适合用于备份
    • RDB非常适合于灾难恢复,作为一个紧凑的单一文件,可以被传输到远程的数据中心
    • RDB在重启保存了大数据集的实例时比AOF要快
    • RDB容易造成数据丢失
    • RDB需要经常调用fork()子进程来持久化到磁盘,数据集很大则fork()比较耗时,重则Redis会停止服务客户端几毫秒甚至一秒。
  2. aof

    • AOF,即Append Only File,以只读的方式追加写命令
    • AOF方式是将执行过的写指令记录下来,在数据恢复时按照从前到后的顺序再将指令都执行一遍
    • 通过配置redis.conf中的appendonly yes就可以打开AOF功能
    • 默认的AOF持久化策略是每秒钟fsync一次(fsync是指把缓存中的写指令记录到磁盘中),此策略下redis仍然可以保持很好的处理性能,即使出现故障也只会丢失最近1秒钟的数据
    • AOF遇到磁盘空间满、inode满或断电等情况导致日志写入不完整也没有关系,redis提供了redis-check-aof工具用来进行日志修复
    • 追加方式如果不做任何处理的话AOF文件会变得越来越大,为此redis提供了AOF文件重写(rewrite)机制,即当AOF文件的大小超过所设定的阈值时redis就会启动AOF文件的内容压缩,只保留可以恢复数据的最小指令集
      • 举个例子:假如我们调用了100次INCR指令,在AOF文件中就要存储100条指令,这明显是很低效的,完全可以把这100条指令合并成一条SET指令,这就是重写机制的原理
    • AOF重写时采用先写临时文件,全部完成后再替换的流程,所以断电、磁盘满等问题都不会影响AOF文件的可用性,重写过程如下:
      • 在重写即将开始之际,redis会创建(fork)一个重写子进程,这个子进程会首先读取现有的AOF文件,并将其包含的指令进行分析压缩并写入到一个临时文件中。
      • 与此同时主工作进程会将新接收到的写指令一边累积到内存缓冲区中,一边继续写入到原有的AOF文件中,这样做是保证原有的AOF文件的可用性,避免在重写过程中出现意外。
      • 当“重写子进程”完成重写工作后,它会给父进程发一个信号,父进程收到信号后就会将内存中缓存的写指令追加到新AOF文件中。
      • 当追加结束后,redis就会用新AOF文件来代替旧AOF文件,之后再有新的写指令,就都会追加到新的AOF文件中了。
    • 灾难恢复的能力
      • 在操作redis时不小心执行了FLUSHALL导致redis内存中的数据全部被清空了,只要redis配置了AOF持久化方式,且AOF文件还没有被重写,此时可以用最快的速度暂停redis并编辑AOF文件,将最后一行的FLUSHALL命令删除,然后重启redis,就可以恢复redis的所有数据到FLUSHALL之前的状态了
    • AOF文件要比RDB文件的体积大,AOF方式的恢复速度也要慢于RDB方式
    • 执行BGREWRITEAOF命令,那么redis会生成一个全新的AOF文件,其中便包括了可以恢复现有数据的最少的命令集。
    • AOF文件被写坏的情况,redis并不会贸然加载这个有问题的AOF文件,而是报错退出,可以通过以下步骤来修复出错的文件:
      • 备份被写坏的AOF文件
      • 运行redis-check-aof –fix进行修复
      • 用diff -u来看下两个文件的差异,确认问题点
      • 重启redis,加载修复后的AOF文件