Redis提供了两种持久化方式,一种是基于快照形式的RDB,另一种是基于日志形式的AOF,每种方式都有自己的优缺点,本文将介绍Redis这两种持久化方式,希望阅读本文后你对Redis的这两种持久化方式有更加全面、清晰的认识。
RDB快照方式持久化
先从RDB快照方式聊起,RDB是Redis默认开启的持久化方式,并不需要我们单独开启,先来看看跟RDB相关的配置信息:
################################SNAPSHOTTING##################################SavetheDBondisk:##save<seconds><changes>##WillsavetheDBifboththegivennumberofsecondsandthegiven#numberofwriteoperationsagainsttheDBoccurred.##Intheexamplebelowthebehaviourwillbetosave:#after900sec(15min)ifatleast1keychanged#after300sec(5min)ifatleast10keyschanged#after60secifatleast10000keyschanged#save""#自动生成快照的触发机制中间的是时间,单位秒,后面的是变更数据60秒变更10000条数据则自动生成快照save9001save30010save6010000#生成快照失败时,主线程是否停止写入stop-writes-on-bgsave-erroryes#是否采用压缩算法存储rdbcompressionyes#数据恢复时是否检测RDB文件有效性rdbchecksumyes#ThefilenamewheretodumptheDB#RDB快照生成的文件名称dbfilenamedump.rdb#快照生成的路径AOF也是存放在这个路径下面dir.
关于RDB相关配置信息不多,需要我们调整的就更少了,我们只需要根据自己的业务量修改生成快照的机制和文件存放路径即可。
RDB有两种持久化方式:手动触发和自动触发,手动触发使用以下两个命令:
- save:会阻塞当前Redis服务器响应其他命令,直到RDB快照生成完成为止,对于内存比较大的实例会造成长时间阻塞,所以线上环境不建议使用
- bgsave:Redis主进程会fork一个子进程,RDB快照生成有子进程来负责,完成之后,子进程自动结束,bgsave只会在fork子进程的时候短暂的阻塞,这个过程是非常短的,所以推荐使用该命令来手动触发
除了执行命令手动触发之外,Redis内部还存在自动触发RDB的持久化机制,在以下几种情况下Redis会自动触发RDB持久化:
- 在配置中配置了save相关配置信息,如我们上面配置文件中的save6010000,也可以把它归类为“savemn”格式的配置,表示m秒内数据集存在n次修改时,会自动触发bgsave。
- 在主从情况下,如果从节点执行全量复制操作,主节点自动执行bgsave生成RDB文件并发送给从节点
- 执行debugreload命令重新加载Redis时,也会自动触发save操作
- 默认情况下执行shutdown命令时,如果没有开启AOF持久化功能则自动执行bgsave
上面就是RDB持久化的方式,可以看出save命令使用的比较少,大多数情况下使用的都是bgsave命令,所以这个bgsave命令还是有一些东西,那接下来我们就一起看看bgsave背后的原理,先从流程图开始入手:
bgsave命令大概有以下几个步骤:
- 1、执行bgsave命令,Redis主进程判断当前是否存在正在执行的RDB/AOF子进程,如果存在,bgsave命令直接返回不在往下执行。
- 2、父进程执行fork操作创建子进程,fork操作过程中父进程会阻塞,fork完成后父进程将不在阻塞可以接受其他命令。
- 3、子进程创建新的RDB文件,基于父进程当前内存数据生成临时快照文件,完成后用新的RDB文件替换原有的RDB文件,并且给父进程发送RDB快照生成完毕通知
上面就是bgsave命令背后的一些内容,RDB的内容就差不多了,我们一起来总结RDB持久化的优缺点,RDB方式的优点:
- RDB快照是某一时刻Redis节点内存数据,非常适合做备份,上传到远程服务器或者文件系统中,用于容灾备份
- 数据恢复时RDB要远远快于AOF
有优点同样存在缺点,RDB的缺点有:
- RDB持久化方式数据没办法做到实时持久化/秒级持久化。我们已经知道了bgsave命令每次运行都要执行fork操作创建子进程,属于重量级操作,频繁执行成本过高。
- RDB文件使用特定二进制格式保存,Redis版本演进过程中有多个格式的RDB版本,存在老版本Redis服务无法兼容新版RDB格式的问题
如果我们对数据要求比较高,每一秒的数据都不能丢,RDB持久化方式肯定是不能够满足要求的,那Redis有没有办法满足呢,答案是有的,那就是接下来的AOF持久化方式
AOF文件持久化方式
Redis默认并没有开启AOF持久化方式,需要我们自行开启,在redis.conf配置文件中将appendonlyno调整为appendonlyyes,这样就开启了AOF持久化,与RDB不同的是AOF是以记录操作命令的形式来持久化数据的,我们可以查看以下AOF的持久化文件appendonly.aof
*2$6SELECT$10*3$3set$6mykey1$6你好*3$3set$4key2$5hello*1$8
大概就是长这样的,具体的你可以查看你Redis服务器上的appendonly.aof配置文件,这也意味着我们可以在appendonly.aof文件中国修改值,等Redis重启时将会加载修改之后的值。看似一些简单的操作命令,其实从命令到appendonly.aof这个过程中非常有学问的,下面时AOF持久化流程图:
在AOF持久化过程中有两个非常重要的操作:一个是将操作命令追加到AOF_BUF缓存区,另一个是AOF_buf缓存区数据同步到AOF文件,接下来我们详细聊一聊这两个操作:
1、为什么要将命令写入到aof_buf缓存区而不是直接写入到aof文件?
我们知道Redis是单线程响应,如果每次写入AOF命令都直接追加到磁盘上的AOF文件中,这样频繁的IO开销,Redis的性能就完成取决于你的机器硬件了,为了提升Redis的响应效率就添加了一层aof_buf缓存层,利用的是操作系统的cache技术,这样就提升了Redis的性能,虽然这样性能是解决了,但是同时也引入了一个问题,aof_buf缓存区数据如何同步到AOF文件呢?由谁同步呢?这就是我们接下来要聊的一个操作:fsync操作
2、aof_buf缓存区数据如何同步到aof文件中?
aof_buf缓存区数据写入到aof文件是有linux系统去完成的,由于Linux系统调度机制周期比较长,如果系统故障宕机了,意味着一个周期内的数据将全部丢失,这不是我们想要的,所以Linux提供了一个fsync命令,fsync是针对单个文件操作(比如这里的AOF文件),做强制硬盘同步,fsync将阻塞直到写入硬盘完成后返回,保证了数据持久化,正是由于有这个命令,所以redis提供了配置项让我们自行决定何时进行磁盘同步,redis在redis.conf中提供了appendfsync配置项,有如下三个选项:
#appendfsyncalwaysappendfsynceverysec#appendfsyncno
- always:每次有写入命令都进行缓存区与磁盘数据同步,这样保证不会有数据丢失,但是这样会导致redis的吞吐量大大下降,下降到每秒只能支持几百的TPS,这违背了redis的设计,所以不推荐使用这种方式
- everysec:这是redis默认的同步机制,虽然每秒同步一次数据,看上去时间也很快的,但是它对redis的吞吐量没有任何影响,每秒同步一次的话意味着最坏的情况下我们只会丢失1秒的数据,推荐使用这种同步机制,兼顾性能和数据安全
- no:不做任何处理,缓存区与aof文件同步交给系统去调度,操作系统同步调度的周期不固定,最长会有30秒的间隔,这样出故障了就会丢失比较多的数据。
这就是三种磁盘同步策略,但是你有没有注意到一个问题,AOF文件都是追加的,随着服务器的运行AOF文件会越来越大,体积过大的AOF文件对redis服务器甚至是主机都会有影响,而且在Redis重启时加载过大的AOF文件需要过多的时间,这些都是不友好的,那Redis是如何解决这个问题的呢?Redis引入了重写机制来解决AOF文件过大的问题。
3、Redis是如何进行AOF文件重写的?
RedisAOF文件重写是把Redis进程内的数据转化为写命令同步到新AOF文件的过程,重写之后的AOF文件会比旧的AOF文件占更小的体积,这是由以下几个原因导致的:
- 进程内已经超时的数据不再写入文件
- 旧的AOF文件含有无效命令,如delkey1、hdelkey2、sremkeys、seta111、seta222等。重写使用进程内数据直接生成,这样新的AOF文件只保留最终数据的写入命令
- 多条写命令可以合并为一个,如:lpushlista、lpushlistb、lpushlistc可以转化为:lpushlistabc。为了防止单条命令过大造成客户端缓冲区溢出,对于list、set、hash、zset等类型操作,以64个元素为界拆分为多条。
重写之后的AOF文件体积更小了,不但能够节约磁盘空间,更重要的是在Redis数据恢复时,更小体积的AOF文件加载时间更短。AOF文件重写跟RDB持久化一样分为手动触发和自动触发,手动触发直接调用bgrewriteaof命令就好了,我们后面会详细聊一聊这个命令,自动触发就需要我们在redis.conf中修改以下几个配置
auto-aof-rewrite-percentage100auto-aof-rewrite-min-size64mb
- auto-aof-rewrite-percentage:代表当前AOF文件空间(aof_current_size)和上一次重写后AOF文件空间(aof_base_size)的比值,默认是100%,也就是一样大的时候
- auto-aof-rewrite-min-size:表示运行AOF重写时AOF文件最小体积,默认为64MB,也就是说AOF文件最小为64MB才有可能触发重写
满足了这两个条件,Redis就会自动触发AOF文件重写,AOF文件重写的细节跟RDB持久化生成快照有点类似,下面是AOF文件重写流程图:
AOF文件重写也是交给子进程来完成,跟RDB生成快照很像,AOF文件重写在重写期间建立了一个aof_rewrite_buf缓存区来保存重写期间主进程响应的命令,等新的AOF文件重写完成后,将这部分文件同步到新的AOF文件中,最后用新的AOF文件替换掉旧的AOF文件。需要注意的是在重写期间,旧的AOF文件依然会进行磁盘同步,这样做的目的是防止重写失败导致数据丢失,
Redis持久化数据恢复
我们知道Redis是基于内存的,所有的数据都存放在内存中,由于机器宕机或者其他因素重启了就会导致我们的数据全部丢失,这也就是要做持久化的原因,当服务器重启时,Redis会从持久化文件中加载数据,这样我们的数据就恢复到了重启前的数据,在数据恢复这一块Redis是如何实现的?我们先来看看数据恢复的流程图:
Redis的数据恢复流程比较简单,优先恢复的是AOF文件,如果AOF文件不存在时则尝试加载RDB文件,为什么RDB的恢复速度比AOF文件快,但是还是会优先加载AOF文件呢?我个人认为是AOF文件数据更全面并且AOF兼容性比RDB强,需要注意的是当存在RDB/AOF时,如果数据加载不成功,Redis服务启动会失败。