为了避免进程退出而造成的数据丢失问题,Redis提供了两种持久化的机制,分别的RDB和AOF。当Redis由于异常退出而重启时,可以利用异常发生之前的持久化文件实现数据恢复。

一、RDB持久化

RDB持久化是把当前进程的数据生成快照,并且把该快照保存到磁盘中的过程。既可以通过命令手动触发RDB的持久化,也可以通过设置规则自动触发RDB的持久化。

可以通过下面命令查看其默认存放路径和文件名

config get dir
config get dbfilename

  • 手动触发

通过redis-cli向redis-server发送save或者bgsave命令




查看redis的状态 查看redis进程_持久化


执行save命令时,日志如下:


查看redis的状态 查看redis进程_redis_02


执行bgsave命令时,我们可以看到下面的日志日志:


查看redis的状态 查看redis进程_子进程_03


  • 自动触发

1、

通过设置save属性,在x秒内发生y次更新,则自动执行一次bgsave命令,该设置可以在redis-cli中通过下面命令查看:

config get save


查看redis的状态 查看redis进程_查看当前redis的进程_04


另外可以同时设置多组规则,只要有其中一组满足即会触发RDB持久化,每两个数组为一组规则

m n 表示m秒内进行了n次更新 则自动执行一次bgsave

RDB持久化之后,存储路径由dir配置项指定,文件名由dbfilename配置项指定,在redis-cli中执行下面命令即可查看当前配置:

config get dir
config get dbfilename


查看redis的状态 查看redis进程_子进程_05


2、

当从节点执行全量复制时,会触发主节点自动执行bgsave命令。先在slave中执行

slaveof no one

会将当前slave与原master失联并且不再是只读模式,可以在master中看到如下日志


查看redis的状态 查看redis进程_子进程_06


然后通过

flushall

清空当前slave实例中的数据,然后重新通过

slave master-host master-port

连接上master,此时master和slave之间恢复连接后会进行全量复制,此时可以在master中看到下面日志,当master决定要进行全量复制后,需要先触发bgsave命令fork一个子进程去持久化rdb文件,当rdb文件dump完成时,将其发送给slave


查看redis的状态 查看redis进程_redis_07


  • 被动触发

1、

当执行

debug reload

命令时,会先触发save命令dump当前内存的rdb文件,save执行完成后先清空当前redis,然后重新加载rdb文件,日志如下


查看redis的状态 查看redis进程_查看redis的状态_08


2、当执行shutdown命令时,如果没有开启aof,则默认会触发save命令

可以通过

config set appendfsync no

关闭当前redis实例的aof持久化,然后执行shutdown命令,通过日志可以看到在redis实例退出前,触发了save命令


查看redis的状态 查看redis进程_redis_09


  • bgsave流程

通过上面可以了解redis在什么时候会进行rdb方式的持久化操作,save命令由于是同步的所以不需要多关注,而bgsave命令是异步的,异步就代表复杂,所以需要来简单了解下bgsave生成rbd文件的流程:


查看redis的状态 查看redis进程_子进程_10


  • 当在父进程中执行bgsave命令时,父进程首先会检测当前是否有子进程正则工作,如果有则直接忽略当前bgsave任务
  • 如果没有其他子进程则先同步阻塞fork一个子进程,可以通过

info stats

命令查看lastest_fork_usec选项来查看当前redis实例最近的一个fork动作耗时,以微秒为单位


查看redis的状态 查看redis进程_子进程_11


子进程fork完成后就不再阻塞父进程,父进程可以继续处理客户端的命令

  • 子进程根据当前时间点的父进程内存快照,生成rdb文件并且原子替换已有的rdb文件,可以通过

info Persistence

查看rdb_last_*选项查看最近一次save和bgsave动作的相关信息,比如时间戳和执行结果


查看redis的状态 查看redis进程_查看当前redis的进程_12


  • rbd持久化的优点

1、rbd文件存储的是二进制数据,可以通过开启lzf压缩使用远小于快照大小的磁盘空间来持久化数据

2、由于是直接存储快照,所以redis加载rdb文件速率很快

  • rdb持久化的缺点

1、rdb持久化必定是全量持久化,无法增量持久化,即无法做到事实持久化

2、rdb持久化时时间一般都很长,频繁执行会消耗大量资源

3、redis版本迭代过程中,产生了许多互相不兼容的rdb格式,所以要慎重升级生产环境的redis服务器

二、aof持久化

和rdb的关注点不同,aof持久化并不关注内存快照,而是记录从aof持久化开启之后的所有更新命令,在极端情况下可以做到实时持久化。可以通过下面命令开启aof持久化:

config set appendonly yes

当aof从关闭状态到打开状态时可以看到下面日志,和bgsave生成rdb文件一样,aof持久化也是由子进程完成的

另外从日志中可以看到,当打开aof持久化时,初始的aof文件是由aof重写生成的,后面会介绍到aof重写


查看redis的状态 查看redis进程_redis_13


aof持久化文件的存放目录和文件名可以通过下面命令查看

config get dir
config get appendfilename

aof持久化本质上是一种增量备份的概念,假如当前服务器开启了aof持久化,基本过程如下图所示


查看redis的状态 查看redis进程_查看当前redis的进程_14


当开发者通过redis客户端向redis服务器发送一个更新请求,在服务器执行完客户端请求后,会将该更新请求发送到redis-server进程的一个缓冲区中缓存起来,该缓冲区称为aof_buf,aof_buf中的数据会以某种策略以追加的模式写入到磁盘上的aof文件中。

可以通过

config get appendfsync

查看当前缓冲区中命令的落盘策略,默认为everysec


查看redis的状态 查看redis进程_redis_15


开发者可以根据需要从下面的三个策略中选择一个

  • everysec

其实际意义为每次执行完更新命令时,将数据写入aof_buf,然后立即执行一次write将aof_buf写入内核,并且同步线程每秒执行一次fsync将aof文件关联的内核缓冲区中的内容落盘

  • awlays

其实际意义为每次执行完更新命令时,将数据写入aof_buf,然后立即执行一次write将aof_buf写入内核,并且紧接着执行一次fsync将aof文件关联的内核缓冲区中的内容落盘

  • no

其实际意义为每次执行完更新命令时,将数据写入aof_buf,然后立即执行一次write将aof_buf写入内核,剩下的工作就交给操作系统来完成了,至于内核缓冲区中与aof相关联的内容何时落盘,完全不可控,最多是30s

rdb持久化文件中存储的是二进制,而aof持久化文件中存储的是字符串,打开aof文件可以看到下面类似的内容,记录着redis-server最近执行过的更新命令


查看redis的状态 查看redis进程_子进程_16


不过正是由于aof文件中存储的是字符串,所以其体积通常要比rdb文件大许多,而且速度增加的很快,为了解决这个问题,redis提供了aof重写机制。

aof重写简单来说就是将当前redis数据库中的键值对转换成语句,然后写入新的aof文件的操作,由于从当前状态转换成语句时可以进行语句合并,这样新的aof文件会比原文件小得多。比如原aof文件中有:

set a a
delete a

此时数据库中并不存在a这个key的记录,所以重写后的aof文件将不存在这两条语句

又比如原aof文件中有下面几条语句:

lpush a 11
lpush a 22
lpush a 33

数据库中此时键为a的list中有三个值,则重写后会被整合成一条语句

lpush a 11 22 33

aof的重写既可以手动触发也可以自动触发,不过他们都需要的前提是redis服务器开启了aof持久化

  • 手动重写

直接向redis服务器发送bgrewriteaof命令就可以重写已有的aof文件,当手动重写aof文件时可以看到下面日志


查看redis的状态 查看redis进程_持久化_17


  • 自动重写

aof文件的自动重写,由redis服务器的下面两个参数共同控制:

  • 1

auto-aof-rewrite-min-size

只有当前的aof文件大小大于该值时,才可能被重写


查看redis的状态 查看redis进程_持久化_18


67108864 / 1024 / 1024 = 64,即默认阈值为64M,单位字节

不过至于会不会被重写还要根据另外一个的条件来决定

  • 2

auto-aof-rewrite-percentage

该值表示当前aof文件大小相对于上次重写后的aof文件大小的增长百分比,即

auto-aof-rewrite-percentage = (aof_current_size - aof_base_size) / aof_base_size

可以通过

info persistence

查看这几个属性的当前值


查看redis的状态 查看redis进程_redis_19


aof_base_size就是上次重写后的aof文件大小,aof_current_size是当前aof文件的大小。

  • aof持久化流程

aof持久化的基本流程如下:


查看redis的状态 查看redis进程_子进程_20


我们需要注意下面几点:

  • 当执行bgrewriteaof命令时,正在进行aof重写,则当前命令直接返回
  • 当执行bgrewriteaof命令时,正在进行 rdb文件的dump,则会等其完成再执行bgrewriteaof
  • 由于bgrewriteaof是fork出来的子进程完成的,fork使用的是写时复制技术,即只能看到fork一瞬间的数据库内容,所以为了防止bgrewriteaof任务执行时redis服务器所执行的更新命令,这些命令都会被记录到aof重写缓冲区
  • 当新的aof文件生成时,可以原子替换旧的aof文件

三、通过持久化文件恢复数据

只要持有aof和rdb文件中的任何一个,都可以进行数据恢复,其基本流程如下:


查看redis的状态 查看redis进程_子进程_21


从流程图中可以看到如下特性:

  • 无论有没有持久化文件,redis都可以正常启动
  • aof持久化文件加载的优先级要高于rdb文件加载
  • rdb文件的加载日志如下


查看redis的状态 查看redis进程_查看redis的状态_22


  • aof文件的加载日志如下


查看redis的状态 查看redis进程_redis_23