Redis的三种持久化方式-快照持久化(RDB)、AOF持久化、混合持久化及容灾备份、优化方案

Redis为了内部数据的安全考虑,会把本身的数据以文件形式保存到硬盘中一份,在服务器重启之后会自动把硬盘的数据恢复到内存(redis)的里边,数据保存到硬盘的过程就称为“持久化”效果。

redis有两种持久化功能,一种是“快照持久化(RDB)”,一种是“AOF持久化”。

一、RDB持久化,默认持久化方式

Redis数据持久化是将内存中的数据保存到磁盘里,避免数据意外丢失。RDB持久化会生成一个RDB文件,这个RDB文件是一个经过压缩的二进制文件。通过该文件可以还原出Redis数据库中的数据。RDB的持久化可以手动执行,也可以根据服务器配置项定期自动执行。
有两个命令可以创建RDB文件,一个是SAVE,另一个是BGSAVE。
执行SAVE命令会阻塞Redis服务器进程,直到RDB文件创建完成为止,在服务器进程被阻塞期间,服务器不能处理任何命令请求。
执行BGSAVE命令会派生出一个子进程,然后由子进程负责创建RDB文件,服务器父进程继续处理命令请求。
SAVE命令和BGSAVE命令的底层调用的函数都是同一个函数rdbSave,只不过SAVE命令是直接调用这个函数,而BGSAVE会fork()出子进程来调用这个函数。

 RDB文件的载入工作是在服务器启动时自动执行的,只要Redis服务器在启动时检测到RDB文件存在,就会自动载入RDB文件。值得一提的是,Redis服务器在载入RDB文件的期间,会一直处于阻塞状态,直到载入工作完成为止。

BGSAVE命令在不阻塞服务器进程的情况下执行,所以Redis允许用户通过设置服务器的save选项来让服务器每隔一段时间自动执行一次BGSAVE命令

步骤

先创建配置/数据/日志目录

我这边已经配置好了,在redis目录下分别创建,然后redis.conf复制备份了一份

wangteng@192 redis % pwd
/Applications/MxSrvs/bin/redis
wangteng@192 redis % ls
bin        data        log
conf        dump.rdb    redis.conf.bak

编辑redis.conf文件,找到以下配置项并暂时设定。

#放行IP限制
bind  0.0.0.0  //这里不做任何限制
#后台启动
daemonize yes
#日志存储目录及日志文件名
logfile "/Applications/MxSrvs/bin/redis/log/redis.log"
#rdb数据文件名
dbfilename dump.rdb
#rdb数据文件和aof数据文件的存储目录
dir /Applications/MxSrvs/bin/redis/data
#设置密码
requirepass 123456

启动redis

//指定配置文件,后台运行redis
wangteng@wangtengdeMacBook-Pro redis % pwd
/Applications/MxSrvs/bin/redis
wangteng@wangtengdeMacBook-Pro redis % ./bin/redis-server conf/redis.conf 
wangteng@wangtengdeMacBook-Pro redis % ps -ef | grep redis
  501 22617     1   0  9:36上午 ??         0:00.41 ./bin/redis-server 127.0.0.1:6379
  501 22639   473   0  9:38上午 ttys000    0:00.01 grep redis
  501 22636 22438   0  9:37上午 ttys002    0:00.01 ./bin/redis-cli

然后连接redis客户端,随意设置key =>val

wangteng@wangtengdeMacBook-Pro redis % kill -9 22617

重新启动redis,然后连接客户端,执行keys*命令,发现key全部丢失,因为之前的配置没有开启持久化操作。因为这个时间比较短,短与900秒,接下来看下面的配置。

继续编辑redis.conf,找到save关键字

save 900 1
save 300 10
save 60 10000
  • 第一条的意思是:服务器在900秒内对数据库执行过至少1次修改,就会执行BGSAVE命令
  • 第二条的意思是:服务器在300秒内对数据库执行过至少10次修改,就会执行BGSAVE命令
  • 第三条的意思是:服务器在60秒内对数据库执行过至少10000次修改,就会执行BGSAVE命令

以上三个save的意思都有了相应的说明,数据修改的频率非常高,备份的频率也高,数据修改的频率低,备份的频率也低。

这里可以把数字调整小一点,比如5,那么然后重启redis,操作命令,5秒中内的所有操作都会被保存。这里可以测试一下,然后data目录下会生成文件,编辑看看

redis-bitsÀ@ú^EctimeÂb:5bú^Hused-memÂо^P^@ú^Laof-preambleÀ^@þ^@û^B^@^@^Bbb^Cbbb^@^Caaa^Caaaÿþ×<93>©^ZÙ<9f><81>

文件有了,那么就算redis重启之后,它会加载文件,自然就不会丢失了.

其实这个文件也可以说是redis快照。产生快照的情况有以下几种

  1. 连接客户端,或者redis-cli bgsave 手动bgsave执行,非阻塞
  2. 手动save执行,阻塞
  3. 根据配置文件自动执行
  4. 客户端发送shutdown,系统会先执行save命令阻塞客户端,然后关闭服务器
  5. 当有主从架构时,从服务器向主服务器发送sync命令来执行复制操作时,主服务器会执行bgsave操作。

如果发现dump.rdb文件缺少了最近的记录,那么在这补充一种手动持久化方式,可以立即看到效果,执行此命令

./redis-cli bgsave          #异步保存

 其次还包括一些其他的手动命令

./redis-cli shutdown    #同步保存到服务器并关闭redis服务器
./redis-cli lastsave    #返回上次成功保存到磁盘的unix时间戳
./redis-cli bgrewriteaof  #当日志文件过长时优化AOF日志文件存储

 由于快照方式是在一定间隔时间做一次的,所以如果redis意外down掉的话,就会丢失最后一次快照后的所有修改。如果应用要求不能丢失任何修改的话,可以采用aof持久化方式

rdb原理图

 rdb优点

  1. 紧凑压缩的二进制文件
  2. fork子进程想能最大化
  3. 启动效率高

rdb缺点

  1. 生成快照的时机问题,频率会造成数据丢失
  2. fork子进程的开销问题,数据很大的情况下会频繁fork子进程生成快照,会造成很大开销,影响性能,如果数据非常多(10-20G)就不合适频繁操作该持久化操作。

 由于快照方式是在一定间隔时间做一次的,所以如果redis意外down掉的话,就会丢失最后一次快照后的所有修改。如果应用要求不能丢失任何修改的话,可以采用aof持久化方式

二、AOF持久化,默认关闭
除了RDB持久化之外,Redis还提供了AOF持久化功能,两者的实现方式有着很大的不同。RDB持久化是通过保存数据库中的键值对来记录数据库状态,而AOF持久化是通过保存Redis服务器所执行的写命令来记录记录数据库状态。
AOF持久化是如何实现的呢?AOF持久化分文三个步骤:命令追加、文件写入、文件同步。

  • 命令追加:当AOF持久化功能打开时,服务器在执行完一个写命令后,会以一定的格式将被执行的写命令追加到服务器中的aof缓冲区中。aof缓冲区是redisServer结构体维护的一个SDS结构的属性。
  • 文件写入:文件写入是指从Redis的aof缓冲区写入到操作系统的内存缓冲区。这个过程是为了提高文件的写入效率,但是带来的风险是服务器出现故障时,内存缓冲区中的数据会丢失掉。
  • 文件同步:这个过程是将内存缓冲区中的数据写入到硬盘中的AOF文件中

Redis中默认执行的是RDB持久化,如何打开AOF持久化呢?我们先来看看AOF的配置项:

步骤

编辑配置文件,增加配置,dir目录可以确保aof和rdb文件都保存在指定目录,所以只需要哦配置一次

#AOF持久化开启
appendonly yes
#AOF文件名称
appendfilename "appendonly.aof"

配置好之后启动redis,那么data目录下就会出现aof文件。

aof同步策略有三种,appendfsync : always、everysec、no三种配置

  1. 每秒同步(默认,每秒调用一次fsync,这种模式想能并不是很糟糕),这个操作由一个线程来负责,性能很高, 每一秒中备份一次。不管一秒钟变化了多少key,只备份一次,性能得到一定的保护。推荐使用。
  2. 每修改同步(会极大削弱redis性能,因为这种模式下每次write后都会调用fsync),可以确保数据不会丢失。一写指令就备份一次。这样做虽然安全,但是系统性能会降低。不推荐使用
  3. 不主动同步(由操作系统自动调度刷磁盘,性能是最好的),这种操作在服务器出现异常时会丢失一部分数据。会查看当前服务器状态,如果状态良好,就进行备份(随机)。这种备份方式数据是没有保证的。

对比下来,性能:always<everysec<no,而数据安全:always>everysec>no。

aof还原数据:服务器只要读入并重新执行一遍AOF文件里保存的写命令,就可以还原服务器关闭之前的数据库内容。

aof数据是基于命令追加,那么假如不小心flushdb的话,找到aof的文件,那么删除最后一行flushdb命令,重启一下redis还是可以恢复数据的。

aof支持重写

redis可以在aof文件体积变得过大时,自动地在后台对aof进行rewrite。即redis以append模式不断的将需改数据写入到老的磁盘文件中,同时redis还会创建一个新的文件用于记录此期间有哪些修改命令被执行。

因为redis在创建新的aof文件过程中,会继续将命令追加到现有的aof文件里面,即使重写过程中发生停机,现有的aof文件也不会丢失。

而一旦aof文件创建完毕,redis就会从旧aof文件切换新的aof文件,并开始对新的aof文件进行追加操作。

为什么要重写?

比如业务很简单,就来回delete set同一个key,就这个业务反复运行了10年,那么aof文件将记录无数个 delete k1,set k1操作,其实都是重复的,但每次追加,文件就越来越大,这个时候假如redis宕机, 需要恢复,那么假如1T的aof文件去恢复,就卡死了。所以压缩重写就是为了减少命令个数,两个命令即可完成。

rewrite触发条件

  • 客户人为执行端执行bgrewriteaof命令
  • auto-aof-rewrite-min-size 64mb,意思是当文件超过64M,redis就开始重写,也可以配置的大一点,可以记录更多内容
  • auto-aof-rewrite-percentage 100,这是配置百分比,意思就是大于上次重写大小2倍的时候重写

常用配置

#fsync 持久化策略
appendfsync everysec
#aof重写期间是否禁止fsync:如果开启该选项,可以减轻文件重写时CPU和硬盘的负载(尤其是硬盘),但是可能会丢失aof重写期间的数据,小在负载和安全性之间进行平衡。如果是yes,那么重写期间是不会记录写入命令,可能会导致重写期间的数据
no-appendfsync-on-rewrite no
#当aof文件大于多少字节后才触发重写
auto-aof-rewrite-min-size 64mb
#当前写入日志文件的大小超过上一次rewrite之后的文件大小的百分之100时,也就是2倍时触发rewrite
#auto-aof-rewrite-percentage 100
#如果aof文件结尾损坏,redis启动时是否载入aof文件,如果文件坏了,会先去修复,再去启动,相当于提示
aof-load-truncated yes

aof优点

  1. 数据不易丢失
  2. 自从重写机制
  3. 易懂易恢复

aof缺点

  1. aof文件恢复数据慢
  2. aof持久化效率低,因为每秒都会记录操作

三、RDB和AOF区别和联系,以及同时工作时的情况

区别和联系:
RDB持久化:默认开启;全量备份,一次性保存整个数据库;体积小,数据恢复快;服务器异常时可能会丢失部分数据;SAVE操作会阻塞,BGSAVE不阻塞。类似于mysql的dump备份文件
AOF持久化:默认关闭;增量备份,一次保存一个修改数据库的命令;体积大,数据恢复慢;备份频率可以自己设置;不会出现阻塞。类似mysql的binlog。

RDB与AOF混合使用

同时开启

redis先加载aof文件来恢复原始数据,因为aof数据比rdb更完整,但是aof存在潜在bug,如把错误的操作记录写入了aof,会导出数据恢复失败,所以可以把rdb作为后备数据。

为了考虑性能,可以只在slave上开启rdb,并且15min备份一次,如果为了避免aof rewrite的IO以及阻塞,可以在redis集群中不开启aof,靠集群的备份机制来保证可用性,在启动时选择较新的rdb文件,如果集群全部崩溃,会丢失15min前的数据。

混合模式

设置redis4.0开始的新特性,在混合使用中AOF读取RDB数据重建原始数据集,集二者优势为一体。

解决问题:redis在重启时通常加载aof文件,但是加载速度慢,因为rdb数据不完整,所以加载aof。

开启方式:aof-use-rdb-preamble true

开启后,aof在重写时会直接读取rdb的内容

运行过程:通过bgwriteaof完成,不同的是当开启混合持久化后

  1. 子进程会把内存中的数据以rdb方式写入aof中
  2. 把重写缓存区中的增量命令以aof方式写入到文件
  3. 将含有rdb和aof的aof数据覆盖旧的aof文件

新的aof文件中,一部分数据来自rdb文件,一部分来自redis运行过程时的增量数据

数据恢复

当开启了混合持久化,启动redis依然优先加载aof文件,aof文件加载可能有两种情况

aof文件开头是rdb的格式,先加载rdb内容再加载剩余的aof

aof文件开头不是rdb格式,直接以aof格式加载整个文件

优点:技能快速备份又能避免大量数据丢失

缺点:rdb是压缩格式,aof在读取它时可读性较差

二者动态切换

redis在2.2或者以上版本,可在不重启的情况下,通过config set 从rdb动态切换aof。

假设我们的环境rdb是开启的,没有开启aof,现在想把rdb关掉,然后使用aof的持久化。那么首先我们先把rdb文件备份,放在安全的地方,不要删除文件。因为开启aof之后,如果aof是混合使用的状态,可能需要加载rdb的数据,然后紧接着执行下面两条命令。首先开启aof的模式,然后set置空,关闭rdb的持久化方式。

#开启aof
redis-cli config set appendonly yes
#关闭rdb
redis-cli config set save "" 

然后写入命令,确保写命令会被正确的追加到aof文件的末尾,这样aof文件就算是创建成功。当然在创建aof文件的过程中会有一些阻塞,这个是会影响redis性能的,因为创建aof直到命令追加写入完成这个过程会阻塞服务,所以建议不要在高峰期做这样的事情。那么后续的命令就可以写到aof文件。

四、redis容灾备份

首先开启了rdb和aof

创建一个脚本 

#!bin/bash
cur_date=$(data "+%Y%m%d%H%M%S")
rm -rf /usr/local/redis/snapshotting/$cur_date
mkdir -p /usr/local/redis/snapshotting/$cur_date
cp /usr/local/redis/data/dump.rdb /usr/local/redis/snapshotting/$cur_date

del_date=$(date -d -48hour "+%Y%m%d%H%M") //把48小时之前的删除掉
rm -rf  /usr/local/redis/snapshotting/$del_date

然后使用crontab定时执行,以下为每10秒执行一次,生成环境可以调整每小时一次

*/1 * * * * sh /usr/local/redis/bin/redis-rdb-copy-per-hour.sh
*/1 * * * * sleep 10; sh /usr/local/redis/bin/redis-rdb-copy-per-hour.sh
*/1 * * * * sleep 20; sh /usr/local/redis/bin/redis-rdb-copy-per-hour.sh
*/1 * * * * sleep 30; sh /usr/local/redis/bin/redis-rdb-copy-per-hour.sh
*/1 * * * * sleep 40; sh /usr/local/redis/bin/redis-rdb-copy-per-hour.sh
*/1 * * * * sleep 50; sh /usr/local/redis/bin/redis-rdb-copy-per-hour.sh

五、优化方案

独立部署,硬盘优化

redis的rdb和aof文件生成的过程,除了会对内存和硬盘有压力,属于cpu密集型的操作,最好把redis单独部署在一台服务器,不要和其他服务部署在一起,因为本来有大量的磁盘io,再加上redis那么磁盘和cpu可能会受不了,其实就是为了解决子进程频繁的创建开销问题。

硬盘可以根据写入了决定,比如redis有大量写入操作,那么可以考虑使用性能更高的ssd硬盘。

缓存禁用持久化

如果redis定位就是做缓存,那么缓存的数据肯定是在别的地方都有,及时丢失了不要紧。比如有两套缓存,有a缓存和b缓存两套,假如a缓存失效了,可以立马把b缓存顶上去,把这种方案做好就可以了。再说缓存肯定在关系型数据库也有,那么就可以把持久化禁用,直接从关系型数据库拿数据再写入缓存就可以了。

主从模式,从持久化

因为从节点是从主节点读的数据,从节点是从主节点复制的,从节点是不会开启写模式的,只读的,数据都是从主节点复制过去的,每次从主节点复制数据的时候,首先会主节点进行一次bgsave操作,然后和磁盘交互一下,生成rdb快照,然后再把快照发给从节点。假如从节点和主节点进行频繁的复制,那么主节点这边的压力会增大,以为频繁fork子进程进行rdb快照生成。既然是主从模式了,那么可不可以把主节点的持久化方式关闭掉,交给从节点,比如从节点15分钟备份一次,只需在从节点添加一个save 900 1就可以了,甚至可以把aof也给禁用掉,以为master主从已经高可用,数据是不可能丢失的,因为15分钟内数据丢失了,可能只是某一个节点丢失了,但是还有其他节点工作着,所以不可能像以前单节点的数据丢失,所以aof都可以禁用,那么又节省了一大批IO的行为。

优化fork处理

其实就是降低aof的重写频率,因为rdb虽然是子进程fork在做这件事,但是rdb有一个非常长的时间窗口期才会做这件事。开起来aof情况下,比如说文件大小已经超过了64M,那么就会重写,在重写过程中肯定会有影响到性能,那么假如把重写的时间给拉长,比如之前是大于64M会重写,那么把大小的上线调大点,比如3G甚至5G,这样就降低了频率,也会提高性能,降低fork子进程的创建。

还可以修改配置

no-appendfsync-on-rewrite no

改为yes,意思就是aof重写期间不去执行接收正常命令追加的行为,这样它会专心做重写,然后继续接收命令。不好的地方就是重写期间的数据可能会丢失,这样也是根据当前具体生成环境的服务器CPU硬盘等各方面因素来决定的,如果硬盘是能扛得住,服务器应能比较好的,那么就可以忽略,那么就可以不要禁用,正常追加就可以了。

 

  

 

 

posted @ 2017-05-17 17:30  温柔的风  阅读(5885)  评论(0编辑  收藏  举报