1 Redis持久化

  RDB(Redis DataBase)

  AOF(Append Only File)

 

2 RBD

2.1 基本说明

  在指定的时间间隔内将内存中的数据集快照写入磁盘文件,它恢复时将快照文件直接读到内存里。

  在进行持久化的时候,Redis会单独创建(Fork)一个子进程(注意:在fork的时候是阻塞的),内存中的数据被克隆了一份,子进程来进行持久化。这样做的好处是,减少主进程的阻塞时间,最大化redis的效率,缺点是消耗了内存空间。

  它会先将数据写入到一个临时文件中,待本次持久化过程完成了,用这个临时文件替换上次持久化好的文件。整个过程中,主进程是不进行任何IO操作的,这就确保了极高的性能 。如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。RDB的缺点是最后一次持久化后的数据可能丢失(上一次持久化后,新写入的数据在内存中,还没有持久化。系统挂了的话,这些数据就没有了)。它是每次生成一个完整数据的新的文件替换上次的。

  Fork的作用是复制一个与当前进程一样的进程。新进程的所有数据(变量、环境变量‘程序计数器等)数值都和原进程一致,但是是一个全新的进程,并作为原进程的子进程。子进程所占用的内存比会和父进程一模一样,它们有相同的数据。如会造成CPU负担。

  RDB保存的是dump.rdb文件---这个文件最好要备份到其它机械以保障数据的安全

 

2.2 RBD的持久化策略

  默认的配置:

    #在900秒之内有1个keys发生变化时触发持久化
    #300秒之内有10个keys发生变化时触发持久化
    #60秒之内有10000个keys发生变化时触发持久化
    save 900 1
    save 300 10
    save 60 10000

  也就是说RDB持久化是按时间切片进行的,隔断时间进行一次。

 

2.3 RDB相关配置

2.3.1 save

    #设置RDB进行数据库镜像持久化的频率。 保存数据到disk的策略
    #900秒之内有1个keys发生变化时
    #30秒之内有10个keys发生变化时
    #60秒之内有10000个keys发生变化时
    save 900 1
    save 300 10
    save 60 10000

 

2.3.2 rdbcompression

    #在进行RDB镜像备份时,是否进行压缩
    rdbcompression yes

 

2.3.3 dbfilename

    #镜像备份文件的文件名-RDB持久化数据保存的文件名称
    dbfilename dump.rdb

 

2.3.4 dir

    #数据库镜像备份的文件放置的路径-RDB持久化数据保存的文件路径
    #路径跟文件名分开配置是因为 Redis 备份时,先会将当前数据库的状态写入到一个临时文件
    #等备份完成时,再把该临时文件替换为上面所指定的文件
    #而临时文件和上面所配置的备份文件都会放在这个指定的路径当中
    #默认值为 ./  redis启动目录
    dir /var/lib/redis/

 

2.3.5 rdbchecksum yes

   是否进行校验和,是否对rdb文件使用CRC64校验和,默认为"yes",那么每个rdb文件内容的末尾都会追加CRC校验和,利于第三方校验工具检测文件完整性,读取和写入时候,会损失10%性能

 

2.3.6 stop-writes-on-bgsave-error yes
  当持久化出现错误时,是否依然继续进行工作,是否终止所有的客户端write请求。默认设置"yes"表示终止,一旦snapshot(快照)数据保存故障,那么此server为只读服务。如果为"no",那么此次snapshot将失败,但下一次snapshot不会受到影响,不过如果出现故障,数据只能恢复到"最近一个成功点"

 

2.4 save和bsave命令

   save:在redis运行中,我们可以显示的发送一条save命令来拍摄快照。save命令是阻塞命令,也就是当服务器接收了一条save命令之后就会开始拍摄快照,在此期间不会再去处理其他的请求,其他请求会被挂起直到备份结束。

  bgsave:也是立即拍摄快照,有别于save命令,bgsave并不是一条阻塞命令,而是fork一个子线程,然后这个子线程负责备份操作。而父进程继续处理客户端的请求,这样就不会造成阻塞了。可以通过lastsave命令获取最后一次成功执行快照的时间。

 

2.5 内存数据没了后如何恢复数据到内存

  启动redis,就会自动恢复了

 

2.6 持久化期间的数据同步

  主线程只有在fork子进程时才会阻塞,所以在持久化期间依然会提供服务,这就产生了一个问题,RBD持久化要将整个Redis中的数据都拷贝一份进行保存,这个操作必然不是短时间内能够完成的,如果在这个过程中出现了数据的修改,该如何保证数据的一致性?

 

  当bgsave子进程执行持久化时,如果主线程要修改一块数据(例如图中的键值对 C),那么,这块数据就会被复制一份,生成该数据的副本,主线程的修改就会发生在这个副本上,而原内存中的值不变。然后,bgsave 子进程则会把这个副本数据写入 RDB 文件,同时快照写完后这个副本内的数据还会再同步回原来的内存块中,以此来保证内存与RBD快照中的数据一致性

 

 

2.7 怎么停止

  动态所有停止RDB保存规则的方法:

redis-cli config set save ""

 

2.8 优点

   适合大规模的数据恢复

   适合对数据完整性和一致性要求不高

 

2.9 缺点

  在一定间隔时间做一次备份,所以如果redis意外down掉的话,就会丢失最后一次快照后的所有修改。

   Fork的时候,内存中的数据被克隆了一份,大约2倍的膨胀性需要考虑。子进程所占用的内存比会和父进程一模一样,如会造成CPU负担。

 

2.10 小结

  RDB在保存RDB文件时父进程唯一需要做的就是fork出一个子进程,接下来的工作全部由子进程来做,父进程不需要再做其他IO操作,所有RDB持久化方式可以最大化redis的性能。
  与AOF相比,在恢复大的数据集的时候,RDB方式会更快一些。
  数据丢失风险大,RDB需要经常fork子进程来保存数据集到硬盘上,当数据集比较大的时候,fork的过程是非常耗时的,可能导致Redis在一些毫秒级不能响应客户端请求。

3 AOF

3.1基本说明

   以日志的形式来记录每个写操作,将Redis执行过的所有写指令记录下来,写入到文件中(读操作不记录),只许追加文件但不可以改写文件,redis启动之初会读取该文件重新构建数据,换言之,redis重启的话根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。

  fsysnc同步文件操作由专门的文件同步线程来进行的。

  在执行写命令的时候,命令会被写入AOF缓存区。触发持久化的时候,会有一个专门的线程来将AOF缓冲区的命令写入到文件中。

 

3.2持久化策略

  appendfsync配置
    #always 表示每次有写操作都进行同步,everysec 表示每秒同步一次。
    #no表示等操作系统进行数据缓存同步到磁盘, 进行同步

    #everysec 表示每秒同步一次
    # appendfsync always :
    appendfsync everysec
    # appendfsync no

  如果AOF缓冲区的文件同步策略为everysec,fsysnc同步文件操作由专门的文件同步线程每秒调用一次。

  这种做法的问题在于,如果硬盘负载过高,那么fsysnc操作可能会超过1s。如果此时Redis异常退出,会导致数据丢失可能远超过1s。

  为此,Redis的处理策略是这样的:主线程每次进行AOF会对比上次fsync成功的时间;如果距上次不到2s,主线程直接返回;如果超过2s,则主线程阻塞直到fsync同步完成。因此,如果系统硬盘负载过大导致fsync速度太慢,会导致Redis主线程的阻塞;此外,使用everysec配置,AOF最多可能丢失2s的数据,而不是1s

 

3.3相关配置

3.3.1 appendonly

    #redis  默认每次更新操作后会在后台异步的把数据库镜像备份到磁盘,但该备份非常耗时,且备份不宜太频繁
    #redis 同步数据文件是按上面save条件来同步的
    #如果发生诸如拉闸限电、拔插头等状况,那么将造成比较大范围的数据丢失
    #所以redis提供了另外一种更加高效的数据库备份及灾难恢复方式
    #开启append only 模式后,redis 将每一次写操作请求(记录的是写操作命令)都追加到appendonly.aof 文件中
    #redis重新启动时,会从该文件恢复出之前的状态。
    #但可能会造成 appendonly.aof 文件过大,所以redis支持BGREWRITEAOF指令,对appendonly.aof重新整理

    #简单来说就是开启AOF,默认是no
    appendonly no

 

3.3.2 appendfilename

# appendfilename appendonly.aof

 

3.3.3 appendfsync

    #设置对 appendonly.aof 文件进行同步的频率-AOF持久化策略
    #always 表示每次有写操作都进行同步,everysec 表示对写操作进行累积,每秒同步一次。
    #no表示等操作系统进行数据缓存同步到磁盘, 都进行同步

    #everysec 表示每秒同步一次
    # appendfsync always :
    appendfsync everysec
    # appendfsync no

 

3.3.4 no-appendfsync-on-rewrite no
  在aof rewrite期间,是否对aof新记录的append暂缓使用文件同步策略,主要考虑磁盘IO开支和请求阻塞时间。默认为no,表示"不暂缓",新的aof记录仍然会被立即同步
  

3.3.5 auto-aof-rewrite-percentage 100
  当Aof log增长超过指定比例时,重写log file, 设置为0表示不自动重写Aof 日志,重写是为了使aof体积保持最小,而确保保存最完整的数据。
  

3.3.6 auto-aof-rewrite-min-size 64mb
  触发aof rewrite的最小文件尺寸

 

3.4怎么恢复数据

  启动redis自动恢复。AOF启动/修复/恢复(RDB和AOF同时存在时,优先加载AOF来恢复的)

  AOF 文件里边包含了重建 Redis 数据所需的所有写命令,所以 Redis 只要读入并重新执行一遍 AOF 文件里边保存的写命令,就可以还原 Redis 关闭之前的状态。

 

  Redis 读取 AOF 文件并且还原数据库状态的详细步骤如下:

  • 创建一个不带网络连接的的伪客户端( fake client),因为 Redis 的命令只能在客户端上下文中执行,而载入 AOF 文件时所使用的的命令直接来源于 AOF 文件而不是网络连接,所以服务器使用了一个没有网络连接的伪客户端来执行 AOF 文件保存的写命令,伪客户端执行命令的效果和带网络连接的客户端执行命令的效果完全一样的。
  • 从 AOF 文件中分析并取出一条写命令。
  • 使用伪客户端执行被读出的写命令。
  • 一直执行步骤 2 和步骤3,直到 AOF 文件中的所有写命令都被处理完毕为止。

  当完成以上步骤之后,AOF 文件所保存的数据库状态就会被完整还原出来。

 

3.5AOF文件异常

  异常恢复(AOF文件写到一半系统崩掉了,AOF文件里面内容就存在问题)。Redis-check-aof --fix AOF文件名  进行修复,它会把不符合语法条件的语句删除掉

 

3.6 重写

3.6.1 AOF重写介绍

  AOF采用文件追加方式,也就是在之前的文件上继续写(RBD是替换上次的文件),文件会越来越大,为避免出现此种情况,新增了重写机制,当AOF文件的大小超过所设定的阈值时,Redis就会启动AOF文件的内容压缩,只保留可以恢复数据的最小指令集(一条数据一个set语句)。

  AOF文件持续增长而过大时,会fork出一条新进程来将文件重写(也是先写临时文件最后再替换),遍历新进程的内存中数据,每条记录有一条的Set语句,也就是说最后的set语句的条数就等于数据的条数,文件会缩小很多。重写aof文件的操作,并没有读取旧的aof文件,而是将整个内存中的数据库内容用命令的方式重写了一个新的aof文件,这点和快照有点类似。

  Redis会记录上次重写时的AOF大小,默认配置是当AOF文件大小是上次rewrite后大小的一倍且文件大于64M时触发。

  AOF的重写是由Redis主线程之外一个子线程执行,在AOF写入的时候,会重新建立一个AOF重写缓冲区,当用户对数据库进行操作时,会把用户的操作追加到AOF重写缓冲区和AOF缓冲区中,此时AOF文件写入操作会同时从AOF缓冲区和AOF重写缓冲区两个地方读入数据,这样就保证了用户的添加修改操作的不丢失

 

3.6.2 AOF重写的过程

  1)调用fork系统级别函数,复制出完全一致的一个子进程,和主进程共用同一块内存空间(类似浅复制,redis为了节省内存开 销的优化点)。此时主线程阻塞,不接收命令请求

  2)子进程调用aof_rewrite函数(redis客户端执行bgrewriteaof命令最终也是调用此函数)可以创建新的AOF文件去执行重写操作 根据已有数据进行命令的压缩和过期时间的检测并将压缩后的命令写入到新的AOF文件,直到写完

  3)在AOF重写过程中,主进程是可以继续对外服务的,当接收到写命令,写入到AOF缓存后,然后判断此时是否正在执行重写 操作,如果是再将写命令写入到AOF重写缓冲区

  4)当子进程完成对AOF文件重写之后,它会向父进程发送一个完成信号,父进程接到该完成信号之后,会调用一个信号处理函数,该函数完成以下工作,此时主线程阻塞,不接收命令请求
    (1)将AOF重写缓存中的内容全部写入到新的AOF文件中;这个时候新的AOF文件所保存的数据状态和服务器当前的数据库的数据一致;
    (2)对新的AOF文件进行改名,原子的覆盖原有的AOF文件;完成新旧两个AOF文件的替换。到这里才是一次完整的AOF 重写流程
    (3)当这个信号处理函数执行完毕之后,主进程就可以继续像往常一样接收命令请求了。

  5)如果AOF重写失败redis会删除中间临时产物,保证流程的健壮性
  

3.6.3 AOF重写细节

  如上图所示,重写前要记录名为 list的键的状态,AOF 文件要保存五条命令,而重写后,则只需要保存一条命令。

  AOF 文件重写并不需要对现有的 AOF 文件进行任何读取、分析或者写入操作,而是通过读取服务器当前的数据库状态来实现的。首先从数据库中读取键现在的值,然后用一条命令去记录键值对,代替之前记录这个键值对的多条命令,这就是 AOF 重写功能的实现原理。

  在实际过程中,为了避免在执行命令时造成客户端输入缓冲区溢出,AOF 重写在处理列表、哈希表、集合和有序集合这四种可能会带有多个元素的键时,会先检查键所包含的元素数量,如果数量超过 REDISAOFREWRITEITEMSPER_CMD ( 一般为64 )常量,则使用多条命令记录该键的值,而不是一条命令。

  rewrite的触发机制主要有一下三个:

  • 手动调用 bgrewriteaof 命令,如果当前有正在运行的 rewrite 子进程,则本次rewrite 会推迟执行,否则,直接触发一次 rewrite。
  • 通过配置指令手动开启 AOF 功能,如果没有 RDB 子进程的情况下,会触发一次 rewrite,将当前数据库中的数据写入 rewrite 文件。
  • 在 Redis 定时器中,如果有需要退出执行的 rewrite 并且没有正在运行的 RDB 或者 rewrite 子进程时,触发一次或者 AOF 文件大小已经到达配置的 rewrite 条件也会自动触发一次。

 

 

3.7 优点

  数据比较完善。

 

3.8缺点

  相同数据集的数据而言aof文件要远大于rdb文件,恢复速度慢于rdb 

  AOF运行效率要慢于rdb,每秒同步策略效率较好,不同步效率和rdb相同

 

3.9 RDB 和 AOF特点

  antirez 在《Redis 持久化解密》一文中讲述了 RDB 和 AOF 各自的优缺点:

  • RDB 是一个紧凑压缩的二进制文件,代表 Redis 在某个时间点上的数据备份。非常适合备份,全量复制等场景。比如每6小时执行 bgsave 备份,并把 RDB 文件拷贝到远程机器或者文件系统中,用于灾难恢复。
  • Redis 加载 RDB 恢复数据远远快于 AOF 的方式
  • RDB 方式数据没办法做到实时持久化,而 AOF 方式可以做到。