因为Redis是内存数据库,它将自己的数据库状态存储在内存里面,所以如果不想办法将存储在内存中的数据库状态保存到磁盘里面,那么一旦服务器进程退出,服务器中的数据库状态也会消失不见。
Redis为了解决这个问题,提供了两种持久化方式。一种是RDB,另一种是AOF方式。前者会根据规则“定时”将内存中的数据保存储在磁盘上,而后者在每次执行命令后将命令的本身记录下来。
RDB持久化是通过快照(snapshoting)完成的,当符合一定的条件时,Redis会自动将内存中的数据保存到磁盘上。Redis会在以下几种情况下对Redis进行快照:
AOF持久化是通过保存Redis服务器所执行的写命令来记录数据库状态的。
为了提高文件写入效率,在现代操作系统中,当用户调用write函数,将一些数据写入到文件的时候,操作系统通常会将写入数据暂时保存在一个内存缓冲区里面,等到缓冲区的空间被填满,或者超过了指定的时限之后,才真正地将缓冲区中的数据写入到磁盘里面。
缓冲区这种做法虽然提供了效率,但是也为写入数据带来了安全问题,因为如果计算机发生停机,那么保存在内存缓冲区里面的写入数据将会丢失。
为此,系统提供了fsync和fdatasync两个同步函数,它们可以强制让操作系统立即将缓冲区中的数据写入到硬盘里面,从而确保写入数据的安全性。
Redis提供的AOF配置项appendsync写回策略直接决定AOF持久化功能的效率和安全性。
所以想要获得⾼性能,就选择 No 策略; 如果想要得到⾼可靠性保证,就选择 Always 策略;如果允许数据有⼀点丢失,⼜希望性能别受太⼤影响的话,那 么就选择 Everysec 策略。
因为AOF持久化是通过保存被执行的写命令来记录数据库状态的,所以随着服务器运行事件的流逝,AOF文件中的内容会越来越多,文件的体积也会越来越大。
为了解决AOF文件体积膨胀的问题,Redis提供了AOF文件重新功能。通过该功能,Redis服务器可以创建一个新的AOF文件来替代现有的AOF文件。
AOF文件重写并不需要对现有的AOF文件进行任何读取,分析或者写入操作,这个功能是通过读取服务器当前的数据库状态来实现的。
比如服务器对list键执行了以下命令:
RPUSH list “A” “B”
RPUSH list “C”
RPUSH list “D” “E”
LPOP list
LPOP list
RPUSH list “F” “G”
正常情况下需要在AOF文件中写入六条命令。而为了减少AOF文件中的命令数量,首先从数据库中读取键现在的值,然后用一条命令区记录键值对,代替之前记录这个键值对的多条命令,这就是AOF重写功能的实现原理。比如上面这个合并成一条命令为:
RPUSH list “C” “D” “E” “F” “G” 来代替保存在AOF文件中的六条命令。
命令合并又会带来一个新的问题,比如重写程序在处理列表,哈希表,集合,有序集合这四种可能带有多个元素的键时,假设合并后命令太长,那么就会造成客户端输入缓冲区溢出。AOF重写是如何解决这个问题的呢?
在重写之前,会先检查键所包含的元素数量,如果超过了64个元素,那么重写程序会多条命令来记录,并且每条命令设置的元素数量也为64个。
AOF重写数据的一致性:
在重写AOF文件的时候,为了避免客户端阻塞,Redis将AOF重写程序放到子进程里执行,这样可以达到两个目的:
子进程进行AOF重写期间,服务器进程(父进程)可以继续处理命令请求。
子进程带有服务器进程的数据副本,使用子进程而不是线程,可以在避免使用锁的情况下,保证数据的安全性。
不过使用子进程也有一个问题需要解决,因为子进程在进行AOF重写期间,服务器进程还需要继续处理命令请求,而新的命令可能会对现有的数据库状态进行修改,从而使的服务器当前的数据库状态和重写后的AOF文件所保存的数据库状态不一致。
为了解决这种数据不一致的问题,Redis服务器设置了一个AOF重写缓冲区。
在子进程进行AOF重写期间,如果服务器执行了相应的写命令,会吧这些写命令都记录到AOF重写缓冲区里。当子进程完成AOF重写工作之后,它会向父进程发送一个信号,父进程在接到该信号之后,将AOF重写缓冲区中所有的内容写入到AOF文件中,这时新AOF文件所保存的数据库状态将和服务器当前的数据库状态一致。
RDB
优点:
• RDB快照是一个压缩过的非常紧凑的文件,保存着某个时间点的数据集,适合做数据的备份,灾难恢复;
• 可以最大化Redis的的性能,在保存RDB文件,服务器进程只需要fork一个子进程来完成RDB文件的创建,父进程不需要做IO操作;
• 与AOF相比,恢复大数据集的时候会更快;
缺点:
• RDB的数据安全性是不如AOF的,保存整个数据集的过程是比繁重的,根据配置可能要几分钟才快照一次,如果服务器宕机,那么就可能丢失几分钟的数据;
• Redis数据集较大时,fork的子进程要完成快照会比较耗CPU、耗时;
AOF
优点:
• 数据更完整,安全性更高,秒级数据丢失(取决fsync策略,如果是everysec,最多丢失1秒的数据);
• AOF文件是一个只进行追加的日志文件,且写入操作是以Redis协议的格式保存的,内容是可读的,适合误删紧急恢复;
缺点:
• 对于相同的数据集,AOF文件的体积要大于RDB文件,数据恢复也会比较慢;
• 根据所使用的fsync策略,AOF的速度可能会慢于RDB。 不过在一般情况下,每秒fsync的性能依然非常高;
通常来说,应该同时使用两种持久化方案,以保证数据安全。
• 如果数据不敏感,且可以从其他地方重新生成,可以关闭持久化。
• 如果数据比较重要,且能够承受几分钟的数据丢失,比如缓存等,只需要使用RDB即可。
• 如果是用做内存数据,要使用Redis的持久化,建议是RDB和AOF都开启。
• 如果只用AOF,优先使用everysec的配置选择,因为它在可靠性和性能之间取了一个平衡。
当RDB与AOF两种方式都开启时,Redis会优先使用AOF恢复数据,因为AOF保存的文件比RDB文件更完整
重启 Redis 时,我们很少使用rdb 来恢复内存状态,因为会丢失⼤量数据。我们通常使⽤ AOF ⽇志重放,但是重 放 AOF ⽇志性能相对 rdb 来说要慢很多,这样在 Redis 实例很⼤的情况下,启动需要花费很⻓的时间。
Redis 4.0 为了解决这个问题,带来了⼀个新的持久化选项——混合持久化。将 rdb ⽂件的内容和增量的 AOF ⽇志 ⽂件存在⼀起。这⾥的 AOF ⽇志不再是全量的⽇志,⽽是⾃持久化开始到持久化结束的这段时间发⽣的增量 AOF ⽇志,通常这部分 AOF ⽇志很⼩。 于是在 Redis 重启的时候,可以先加载 rdb 的内容,然后再重放增量 AOF ⽇志就可以完全替代之前的 AOF 全量⽂ 件重放,重启效率因此⼤幅得到提升。 所以 RDB 内存快照以稍微慢⼀点的频率执⾏,在两次 RDB 快照期间使⽤ AOF ⽇志记录期间发⽣的所有「写」操 作。 这样快照就不⽤频繁的执⾏,同时由于 AOF 只需要记录两次快照之间发⽣的「写」指令,不需要记录所有的操 作,避免出现⽂件过⼤的情况。