缓存服务器有一个很重要的指标就是能否持久化,如果缓存服务器不支持持久化的话,一些相对重要的数据都不能存在缓存服务器中了,毕竟谁也不能保证服务百分百可用,一旦缓存服务器宕机,所有数据就都丢失了。
今天来分享一下Redis的持久化两种持久化方式RDB和AOF。
1、RDB
RDB(redis database),可以理解为快照/内存快照,RDB持久化过程是将当前进程中的数据生成快照存储到硬盘中。
1.1、触发机制
RDB持久化的触发机制分为两种,手动触发和自动触发。
手动触发
执行save和bgsave两个命令可以手动触发RDB持久化
- save命令会阻塞当前服务器,直到RDB完成为止,如果数据量大的话会造成长时间的阻塞,线上环境一般禁止使用
- bgsave很好理解,就是background save,执行bgsave命令时Redis进程会fork一个子进程来完成RDB的过程,完成后自动结束,所以Redis主进程阻塞时间只有fork阶段的那一下。相对于save,阻塞时间很短。
自动触发
在redis.config配置文件里可以配置自动触发,配置方式如下:
save <seconds> <changes>
- 1
这个配置的规则指的是在seconds秒内发生changes次写操作,就会自动进行一次bgsave,例如:
save 900 1
- 1
指的是如果900秒内有1条Key信息发生变化就会触发一次bgsave。
还有在执行shutdown命令的时候,如果没有开启AOF持久化功能,那么会自动执行一次bgsave。
1.2、RDB执行流程
- 执行bgsave命令的时候,Redis主进程会检查是否有子进程在执行RDB/AOF持久化任务,如果有的话,直接返回。
- Redis主进程会fork一个子进程来执行执行RDB操作,fork操作会对主进程造成阻塞(影响Redis的读写),fork操作完成后会发消息给主进程,从而不再阻塞主进程。
- RDB子进程会根据Redis主进程的内存生成临时的快照文件,RDB完成后会使用临时快照文件替换掉原来的RDB文件。
- RDB子进程完成RDB持久化后会发消息给主进程,通知RDB持久化完成。
上面讲的是RDB持久化执行的主要流程,下面再讲一些细节上的东西:
- 在有子进程执行RDB过程的时候,Redis主进程的读写不受影响,但是对于Redis的写操作不会同步到主进程的主内存中,而是会写到一个临时的内存区域作为一个副本,等到主进程接收到子进程完成RDB过程的消息后再将内存副本中的数据同步到主内存。
- Redis默认采用LZF算法对RDB文件进行压缩,所以生成的内存文件会比内存小很多。
1.3、RDB的优缺点
优点:
- RDB文件小,非常适用于定时备份,用于灾难恢复。
- Redis加载RDB文件的速度比AOF快很多,因为RDB文件中直接存储的时内存数据,而AOF文件中存储的是一条条命令。
缺点:
- RDB无法做到实时持久化,因为fork子进程属于重量级操作,会阻塞Redis主进程。
- 存在老版本的Redis不兼容新版本RDB格式文件的问题。
主要因为RDB持久化不支持实时持久化,只要Redis服务宕机了,那么从上一次bgsave到宕机之间的所有数据都会丢失,所以在数据实时性要求高的情况下不适合使用RDB,所以Redis又提供了AOF持久化
2、AOF持久化
AOF(append only file),以日志的方式记录每次写命令,服务重启的时候重新执行AOF文件中的命令来恢复内存数据。因为解决了数据持久化实时性的问题,所以目前AOF是Redis持久化的主流方式。
2.1、开启AOF
AOF默认是关闭的,可以在redis.conf配置文件中添加下面配置开启AOF
appendonly yes
- 1
2.2、AOF执行流程
- 所有的写命令都会追加到aof_buf(缓冲区)中。
- 可以使用不同的策略将AOF缓冲区中的命令写到AOF文件中。
- 随着AOF文件的越来越大,会对AOF文件进行重写。
- 当服务器重启的时候,会加载AOF文件并执行AOF文件中的命令用于恢复数据。
简单分析一下AOF执行流程中的一些问题:
- 因为Redis为了效率,使用单线程来响应命令,如果每次写命令都追加写硬盘的操作,那么Redis的响应速度还要取决于硬盘的IO效率,显然不现实,所以Redis将写命令先写到AOF缓冲区。
- 写道缓冲区还有一个好处是可以采用不同的策略来实现缓冲区到硬盘的同步,可以让用户自行在安全性和性能方面做出权衡。
2.3、同步策略
在了解同步策略之前,需要先来了解两个三方法flushAppendOnlyFile、write和save:
- redis的服务器进程是一个事件循环,文件事件负责处理客户端的命令请求,而时间事件负责执行serverCron函数这样的定时运行的函数。在处理文件事件执行写命令,使得命令被追加到aof_buf中,然后在处理时间事件执行serverCron函数会调用flushAppendOnlyFile函数进行文件的写入和同步
- write:根据条件,将aof_buf中的缓存写入到AOF文件
- save:根据条件,调用fsync或fdatasync函数将AOF文件保存到磁盘
下面来介绍Redis支持的三种同步策略:
- AOF_FSYNC_NO:不保存(write和read命令都由主进程执行)
- AOF_FSYNC_EVERYSEC:每一秒钟保存一次(write由主进程完成,save由子进程完成)
- AOF_FSYNC_ALWAYS:每执行一个命令保存一次(write和read命令都由主进程执行)
AOF_FSYNC_NO
在这种策略下,每次flushAppendOnlyFile函数被调用的时候都会执行一次write方法,但是不会执行save方法。
只有下面三种情况下才会执行save方法:
- Redis被关闭
- AOF功能被关闭
- 系统的写缓存被刷新(可能是缓存已经被写满,或者定期保存操作被执行)
这三种情况下的save操作都会引起Redis主进程阻塞,并且由于长时间没有执行save命令,所以save命令执行的时候,阻塞时间会很长
AOF_FSYNC_EVERYSEC
在这种策略下,save操作原则上每隔一秒钟就会执行一次, 因为save操作是由后台子线程调用的, 所以它不会引起服务器主进程阻塞。
其实根据Redis的状态,每当 flushAppendOnlyFile函数被调用时,write命令和save命令的执行又分为四种不同情况:
根据以上图知道,在AOF_FSYNC_EVERYSEC策略下, 如果在情况1时发生故障停机, 那么用户最多损失小于2秒内所产生的数据;而如果在情况2时发生故障停机,堆积了很多save命令,那么用户损失的数据是可以超过 2 秒的。
AOF_FSYNC_ALWAYS
在这种模式下,每次执行完一个命令之后,write和save命令都会被执行。
另外,因为save命令是由Redis主进程执行的,所以在save命令执行期间,主进程会被阻塞。
三种策略的优缺点
AOF_FSYNC_NO策略虽然表面上看起来提升了性能,但是会存在每次save命令执行的时候相对长时间阻塞主进程的问题。并且数据的安全性的不到保证,如果Redis服务器突然宕机,那么没有从AOF缓存中保存到硬盘中的数据都会丢失。
AOF_FSYNC_ALWAYS策略的安全性的到了最大的保障,理论上最多丢失最后一次写操作,但是由于每个写操作都会阻塞主进程,所以Redis主进程的响应速度受到了很大的影响。
AOF_FSYNC_EVERYSEC策略是比较建议的配置,也是Redis的默认配置,相对来说兼顾安全性和性能。
2.4、重写机制
随着命令不断从AOF缓存中写入到AOF文件中,AOF文件会越来越大,为了解决这个问题,Redis引入了AOF重写机制来压缩AOF文件。
AOF文件的压缩和RDB文件的压缩原理不一样,RDB文件的压缩是使用压缩算法将二进制的RDB文件压缩,而AOF文件的压缩主要是去除AOF文件中的无效命令,比如说:
- 同一个key的多次写入只保留最后一个命令
- 已删除、已过期的key的写命令不再保留
AOF重写的触发机制也分为手动触发和自动触发两种方式。
手动触发
执行bgrewriteaof命令直接触发AOF重写
自动触发
在redis.config配置文件中有两个配置项
auto-aof-rewrite-min-size 64MB
auto-aof-rewrite-min-percenrage 100
- 1
- 2
上面两个配置表示:
- 当AOF文件小于64MB的时候不进行AOF重写
- 当当前AOF文件比上次AOF重写后的文件大100%的时候进行AOF重写
可以在redis.conf配置文件中添加这两个参数来自动触发AOF重写,执行bgrewriteaof命令
2.5、AOF重写流程
- 执行bgrewriteaof命令的时候,如果当前有进程正在执行AOF重写,那么直接返回;如果有进程正在执行bgsave,那么等待bgsave执行完毕再执行AOF重写。
- Redis主进程会fork一个子进程执行AOF重写,开销和RDB重写一样。
- AOF重写过程中,不影响Redis原有的AOF过程,包括写消息到AOF缓存以及同步AOF缓存中的数据到硬盘。
- AOF重写过程中,主进程收到的写操作还会将命令写到AOF重写缓冲区,注意和AOF缓冲区区分开。
- 由于AOF重写过程中原AOF文件还在陆续写入数据,所以AOF重写子进程只会拿到fork子进程时的AOF文件进行重写。
- 子进程拿到原AOF文件中的数据写道一个临时的AOF文件中。
- 子进程完成AOF重写后会发消息给主进程,主进程会把AOF重写缓冲区中的数据写道AOF缓冲区,并且用新的AOF文件替换旧的AOF文件。
上面讲的是AOF重写的主要流程,下面再讲一些细节上的东西:
- Redis对AOF的重要性看得比RDB重,因为RDB的时候如果有进程正在执行AOF,那么直接返回;而AOF的时候如果有进程正在执行RDB,那么等RDb结束再执行AOF。
- Redis再AOF重写的时候新建一个AOF重写缓冲区的目的是为了保证重写过程中的写命令数据不会丢失。
- 子进程在重写AOF文件的时候,每次写硬盘的数据量由配置决定,不能太大,否则会导致硬盘阻塞(默认32MB)。
- AOF重写的整个过程有三个部分会阻塞进程:
- 主进程fork子进程的时候
- 主进程把AOF重写缓冲区中的数据写到AOF缓冲区的时候
- 使用新的AOF文件替换掉旧的AOF文件的时候
3、Redis重启时加载持久化文件的顺序
- Redis重启的时候优先加载AOF文件,如果AOF文件不存在再去加载RDB文件。
- 如果AOF文件和RDB文件都不存在,那么直接启动。
- 不论加载AOF文件还是RDB文件,只要发生错误都会打印错误信息,并且启动失败。
4、总结
关于Redis的两种持久化方式到这里就介绍完了,这里再总结一下:
- RDB持久化基于内存快照存储二进制文件,AOF持久化基于写命令存储文本文件。
- RDB文件采用了压缩算法,比较小;AOF文件随着命令的叠加会越来越大,Redis提供了AOF重写来压缩AOF文件。
- 恢复RDB文件的速度比AOF文件快很多。
- RDB持久化方式实时性不好,所以AOF持久化更主流。
- 合理的使用AOF的同步策略,理论上不会丢失大量的数据。