说道redis的持久化机制,相信有经验的开发人员可能都知道,rdb,aof。但是不知道大家有没有仔细的去想过几个问题:
(1)rdb和aof到底是如何工作的?
(2)既然有了rdb为什么还要有aof呢?
(3)如果我既设置了rdb又设置了aof,那么到底听谁的?
(4)aof的混合持久化又是怎么回事呢?
下面我主要围绕这几个方面,结合一些具体的案例。来跟大家分享一下我这边的总结心得。
首先我们来看rdb
当然首先得知道rdb是什么东西,如何去设置:
RDB:原理是redis会单独创建(fork)一个与当前进程一模一样的子进程来进行持久化,这个子进程的所有数据(变量。环境变量,程序程序计数器等)都和原进程一模一样,会先将数据写入到一个临时文件中,待持久化结束了,再用这个临时文件替换上次持久化好的文件,整个过程中,主进程不进行任何的io操作,这就确保了极高的性能
rdb系统所有相关配置项如下,这边都做了中文详细解释。如何配置?:
关于redis所有配置详细解释,可以参考我另一篇博文:
https://blog.csdn.net/baomw/article/details/103789050
# 指定在多长时间内,有多少次更新操作,就将数据同步到数据文件,可以多个条件配合
# 这里表示900秒(15分钟)内有1个更改,300秒(5分钟)内有10个更改以及60秒内有10000个更改
# 如果想禁用RDB持久化的策略,只要不设置任何save指令,或者给save传入一个空字符串参数也可以
#由于rdb持久化是需要fork子进程去进行备份操作,非常消耗资源,一般生产使用不会设置太高的频率
#一般只留第一个 save 900 1配置即可
save 900 1
save 300 10
save 60 1
# 默认情况下,如果 redis 最后一次的后台保存失败,redis 将停止接受写操作,这样以一种强硬的方式让用户知道数据不能正确的持久化到磁盘,
# 否则就会没人注意到灾难的发生。 如果后台保存进程重新启动工作了,redis 也将自动的允许写操作。
# 如果配置成no,表示你不在乎数据不一致或者有其他的手段发现和控制
stop-writes-on-bgsave-error yes
# 对于存储到磁盘中的快照(rdb),可以设置是否进行压缩存储。如果是的话,redis会采用
# LZF算法进行压缩。如果你不想消耗CPU来进行压缩的话,可以设置为关闭此功能
rdbcompression yes
# 在存储快照后,还可以让redis使用CRC64算法来进行数据校验,但是这样做会增加大约
# 10%的性能消耗,如果希望获取到最大的性能提升,可以关闭此功能
rdbchecksum yes
#rdb文件的名字。
dbfilename dump.rdb
# dbfilename文件存放目录。必须是一个目录,aof文件也会保存到该目录下。
dir ./
众所周知,redis是单线程的,下面我画了一个单机版的redis执行流程:
据上我们来分下下,redis执行rdb持久化的时候,为什么要fork一个子进程:
因为redis是单线程的,如果这个线程(主线程)去进行rdb持久化了,那么此时有客户端向我们服务端发送命令,他是得不到处理的(唯一的线程在做持久化),必须要等持久化结束后才能接受客户端命令。那么这个过程可以理解为客户端发送命令会阻塞(上图中的阻塞队列),这样所有的客户端请求都得不到及时处理。如果fork一个子进程,就可以保证rdb的异步执行,不影响客户端并行执行。
redis中执行rdb的几种场景如下:
(1)满足配置文件的条件(如15分钟内有一个key改变)
(2)执行shutdown时,如果没有开启aof,会触发一次rdb
(3)执行命令save或者bgsave命令时,但是save或者bgsave有点区别
bgsave:会fork子进程,原理与rdb上面原理一样,系统默认触发rdb持久化都是调用的此命令
save:是不会fork子进程的,它使用主进程进行持久化,所以会导致客户端命令发送到我们服务端得不到及时处理,所以他是阻塞的
(4)执行flushall命令时
下面来看看aof:
原理是将Reids的操作日志以追加的方式写入文件,但是只记录增改操作,读操作是不记录的,
整体分为三步:数据写入内存–》数据写入aof_buf—》写入持久化文件。
第二步到第三步什么时候执行根据配置文件触发机制(注意:aof持久化不会fork子进程)
apf相关redis配置如下:
############################## APPEND ONLY MODE ###############################
# 是否启用aof持久化方式 。否在每次更新操作后进行日志记录,Redis在默认情况下是异步的把数据写入磁盘,如果不开启,可能会在断电时导致一
# 段时间内的数据丢失。
# 因为 redis本身同步数据文件是按上面save条件来同步的,所以有的数据会在一段时间内只存在于内存中。默认为no
appendonly no
# The name of the append only file (default: "appendonly.aof")
# 指定更新日志(aof)文件名,默认为appendonly.aof
appendfilename "appendonly.aof"
#指定更新日志条件,共有3个可选值:
# no:表示等操作系统进行数据缓存同步到磁盘(快,持久化没保证)
# always:同步持久化,每次发生数据变更时,立即记录到磁盘(慢,安全)
# everysec:表示每秒同步一次(默认值,很快,但可能会丢失一秒以内的数据,一般生产环境使用此配置即可)
# appendfsync always
appendfsync everysec
# appendfsync no
# 指定是否在后台aof文件rewrite期间调用fsync,默认为no,表示要调用fsync(无论后台是否有子进程在刷盘)。
# Redis在后台写RDB文件或重写AOF文件期间会存在大量磁盘IO,此时,在某些linux系统中,调用fsync可能会阻塞。
#如果应用系统无法忍受延迟,而可以容忍少量的数据丢失,则设置为yes。如果应用系统无法忍受数据丢失,则设置为no。
no-appendfsync-on-rewrite no
#当AOF文件增长到一定大小的时候Redis能够调用 BGREWRITEAOF 对日志文件进行重写 。
#当AOF文件大小的增长率大于该配置项时自动开启重写。
auto-aof-rewrite-percentage 100
#当AOF文件增长到一定大小的时候Redis能够调用 BGREWRITEAOF 对日志文件进行重写 。当AOF文件大小大于该配置项时自动开启重写
#一般生产环境会修改此配置,至少3G-5G起,不会64M就重写一次
auto-aof-rewrite-min-size 64mb
#是否开启混合持久化
aof-use-rdb-preamble yes
aof重写机制
of是以日志追加的方式将命令字符串协议保存在aof 文件中,随着我们使用redis的时间越长,最redis的操作越多,这个aof文件会越来越大,如果不做处理,总有会撑爆磁盘,所以就出现了重写,重写就是专门给aof文件廋身的
他的思想是:直接根据现在内存的数据,生成新的aof文件,然后去替换旧的aof文件,就可以把一下没用字符去掉,区别我们的mvcc,重写只关注最终结果,比如set k1 v1 ,然后我们del k1等等一些没用操作,这样我们的文件大小就会小很多
当然重写的触发时机也是通过配置来设置的,可以参看上面的配置注释,都有详细说明
注意:重写操作是通过fork子进程来完成的,所以正常的aof不会fork子进程,触发了重写才会
那么问题来了,如果我们redis既开启了rdb,有开启了aof,那么听谁的?这里要分两步来分析
1,运行过程,都起作用,各司其职,很好理解
2,启动过程,关于启动过程,我这边画了一个启动时数据加载流程,如下:
解释一下,redis启动时数据加载,如果开启了aof,优先考虑aof,只加载aof文件,没开起aof则加载rdb文件,肯定有人问为什么?通过两者同步机制便知,rdb执行周期长,数据缺失多,aof执行周期短,数据缺失少,使用默认配置,只会丢失一秒内的数据。所以加载时以aof文件为主,尽可能保障了停机前数据恢复的完整性!
这点也说明了本文一开始提到的问题2,既然有了rdb为什么还要有aof呢?正是因为rdb执行周期长,所以出现了aof这种持久化机制,尽可能的保障了数据备份的完整性!
那么可能有人会问?既然aof是一秒同步一次,那么我将rdb配置成:save 1 1 不也是可以达到一秒同步一次,那跟aof有什么区别呢?
其实不然,我们前面提到过,rdb是需要fork子线程去执行持久化操作,而aof是直接使用主线程区执行持久化,rdb是备份完整数据,而aof只是保存记录,在主线程fork子线程的时候会带来大量的cpu开销,其实是很消耗性能的,这样你的redis每时每刻都在做着废性能的fork子进程操作,redis又谈何高性能呢?所以这是不可取的!
redis4.0后混合持久化机制
4.0版本的混合持久化,但是这个设置默认是关闭的,可以通过如下设置开启我们的混合持久化
#yes则表示开启,no表示禁用,5.0之后默认开启。
aof-use-rdb-preamble yes
混合持久化是通过bgrewriteaof完成的,不同的是当开启混合持久化时,fork出的子进程先将共享的内存副本全量的以RDB方式写入aof文件,然后在将重写缓冲区的增量命令以AOF方式写入到文件,写入完成后通知主进程更新统计信息,并将新的含有RDB格式和AOF格式的AOF文件替换旧的的AOF文件。
简单的说:新的AOF文件前半段是RDB格式的全量数据后半段是AOF格式的增量数据
优点:混合持久化结合了RDB持久化 和 AOF 持久化的优点, 由于绝大部分都是RDB格式,加载速度快,同时结合AOF,增量的数据以AOF方式保存了,数据更少的丢失。
缺点:兼容性差,一旦开启了混合持久化,在4.0之前版本都不识别该aof文件,同时由于前部分是RDB格式,二进制文件,阅读性较差(反正我是看不懂)
性能建议:
上面讲完了redis的几种持久化方式,但是具体到实际生产环境中,改如何去选择呢?可以参考如下建议:
rdb和aof同时存在时,因为RDB文件只用作后备用途,只要15分钟备份一次就够了,只保留save 900 1这条规则。
重写:只要硬盘许可,应该尽量减少AOF rewrite的频率,AOF重写的基础大小默认值64M太小了,可以设到3G - 5G以上。默认超过原大小100%大小时重写可以改到适当的数值。
好了关于redis的持久化机制这边就先讲到这里,如有不当之处,可以留言指正,谢谢!