redis持久化有两种方式:RDB和AOF。分别对应着全量复制和增量复制。深入理解各自的实现方式及适用场景对redis的使用和运维十分重要。下面就分别介绍。html
RDB持久化即将当前Redis实例中的数据快照所有保存的磁盘的过程。可手动触发,也可根据配置自动触发。redis
手动触发有两个命令能够选择: save
和bgsave
。二者区别在于save
是阻塞的,复制完成前会阻塞客户端命令执行,目前已经废弃。bgsave
是非阻塞的。执行过程当中redis进程会fork出一个子进程负责复制任务,而主进程依然响应客户端请求命令。对应bgsave
命令,阻塞只存在于fork
过程当中。大大减少了阻塞的时间。缓存
如图,上次rdb同步时间在17:20安全
执行命令bgsave
后, 返回 Background saving started
,bash
查看rdb文件更新时间,已变为当前时间。服务器
若使用自动触发,需配置save参数。格式 save m n
。查看redis配置文件可看到相关配置。app
save 900 1
save 300 10
save 60 10000
复制代码
配置 save m n
含义为当数据在m秒内发生n次修改,自动执行bgsave
命令进行RDB持久化。如save 900 1
表示在900秒(15分钟内)数据有至少1次修改,则触发RDB。多个配置状况下,只要符合任一条件便可。运维
具体在实现上,redis服务器会记录自上次RDB备份后,有多少次修改。每100ms检查一次,看是否有符合条件的save配置,有则再次执行DRB.性能
另外,当在客户端执行关闭服务器命令shutdown
,若redis没有开启AOF,则自动执行bgsave
进行备份。学习
因为save的持久化命令会发送阻塞,因此目前基本是用bgsave命令触发的RDB,关于bgsave的流程以下图所示。
简要流程以下
bgsave
命令后,调用fork命令建立子进程。期间主进程是阻塞的(图中1,2)。Background saving started
,再也不阻塞,照常响应客户端命令。(图中3)简要来讲是父进程fork出了一个子进程用于持久化任务,父进程仍响应客户端请求,耗时的RDB让子进程来作。这样的确能够大大减小阻塞时间,可有一个问题是,主进程还在接受请求,子进程怎样进行复制?难道子进程再复制一份主进程的内存用于复制,这样内存岂不是要翻倍?确定不能这样。要是子进程共享父进程的内存,那怎样保证边接受请求边复制呢?
事实上,fork操做使用写时复制(copy-on-write)技术实现。建立的子进程共享父进程地址空间,当只有须要写入时才会复制地址空间。也就是说,redis的子进程使用共享的内存快照完成RDB备份,而主进程当收到写请求后,会将涉及到的内存页复制出一份副本,在此副本进行修改。当子进程RDB完成后,通知主进程更换副本,RDB就此完成。
dir
设置,文件名由dbfilename
指定。RDB文件为二进制文件,可开启压缩处理,压缩后文件体积可大大缩小。使用rdbcompression
参数指定,默认为yes
。如下是redis配置文件有关rdb的默认配置
## 是否开启rdb文件压缩
rdbcompression yes
## rdb文件效验
rdbchecksum yes
## rdb文件名
dbfilename redis-dump-127.0.0.1-6379.rdb
## 备份文件储存目录
dir /usr/local/var/db/redis/
复制代码
AOF主要用于解决redis实时持久化的问题。方式为实时将redis全部写命令记录下来,用于之后恢复及备份。
aof默认是关闭的,使用appendonly yes
开启。用appendfilename
设置aof文件名。同rdb同样,dir
表示aof储存目录。
在配置文件设好后重启redis,即开启了aof功能。可以使用info persistence
可查看redis持久化的参数,以下
aof_enabled:1
表示已开启aof功能。
AOF写入日志的直接就是文本格式。如set hello world
这个命令,在appendonly.aof
文件中储存的就是
*3
$3
SET
$5
hello
$5
world
复制代码
这就是Redis客户端与服务器通讯的的序列化协议(RESP),每行用\r\n
分割,*n表示参数个数,$n表示字节数,十分简单明了。AOF直接使用协议格式可以让redis直接识别。无需特殊处理,避免二次处理的开销,也方便修改。
AOF的流程以下
用流程图表示就是
为何不直接将命令写入磁盘很容易理解,这里需关注的是以什么策略从缓冲区写到磁盘。redis提供了3种策略。
策略 | 说明 |
---|---|
always | 每次命令写入缓冲区后都同步刷新到硬盘(使用fsync命令刷新磁盘),此方式最安全,但也是最慢的,由于每次写数据都要同步硬盘 |
everysec | 每次写命令调用系统命令write操做,write只会将数据写入系统缓冲区,而后由专门的线程每秒同步刷盘 |
no | 每次写命令调用系统命令write操做写入系统缓冲区,具体刷盘时间由操做系统决定,性能是最高的,但数据安全性不能保证 |
一般,咱们选择everysec
做为刷盘策略,也是redis默认配置,能够兼顾性能和数据安全。
通常咱们使用everysec
的同步刷盘策略。此时会有一个专门的线程每秒执行一次刷盘操做。为保证数据安全性,redis主进程会比对上次刷盘时间与当前时间的差值,若是大于2秒,则阻塞以等待刷盘完成。
也就是说,若是硬盘性能不好,fsync
执行太慢,会形成redis阻塞。以保证硬盘的数据不被真实内存数据落的太远(最大2秒的数据差)。
刚才提到,AOF直接使用redis的序列化协议进行备份,一段时间后,aof文件会很大。为解决此问题,可配置aof重写机制对原aof文件进行瘦身。
AOF重写的实质即把redis进程的内存数据转为写命令从新生成一份aof文件,以替代原aof文件。
重写后比原aof体积小的缘由有如下几点
hset k1 f1 v1
,hset k1 f2 v2
可合并hmset k1 f1 v1 f2 v2
)set hello a, set hello b
等)AOF可有手动触发和自动触发。
手动触发使用bgrewriteaof
命令触发。
自动触发有关参数及含义以下
## aof重写时此时aof文件与上次aof文件大小百分比
auto-aof-rewrite-percentage 100
## aof重写时文件最小大小,默认64M
auto-aof-rewrite-min-size 64mb
复制代码
如按默认配置,则发生重写的条件为aof文件大于64M且此时aof文件比上次重写时大100%,便是上次的2倍。
aof重写虽然比较复杂,但对比图,也能很容易明白其中流程。这这里有个疑问是为何主进程在子进程重写时为何要维护两个缓冲区?只维护一个不行吗?毕竟原有aof文件等到新aof文件生成后也是要替换的。
仔细想一想就可知,有必要维护两个缓冲区。主要是须要维护原有aof逻辑不变,以防aof重写失败致使数据丢失。另外一点两个缓冲区目的也不一样,不宜混淆。
注意:
为防止资源过于竞争,同一时刻只能进行一个AOF重写任务,当进行重写时发现有RDB任务时,也会等RDB完成后进行。
在重启时,redis会首先判断是否开启aof,若开启aof且aof文件存在,则加载aof文件进行数据恢复以启动。不然判断rdb文件存在并加载rdb以启动。
在进行RDB备份或是AOF重写时,都须要fork出子进程运行任务。此期间会大量消耗CPU,一般子进程对单核CPU的利用率达到90%,所以在有RDB或需AOF重写任务的redis,不要作绑定单核CPU操做,这会致使父子进程对CPU产生竞争。