因为redis是基于内存保存数据,如果宕机,就会丢失数据。
因此redis提供了两种持久化机制
- RDB,基于快照
- AOF,基于日志
什么是RDB
RDB -- redis会将数据集的快照dump到dump.rdb文件,做一个全量备份用于恢复。
Redis如何一边响应请求,一边持久化?
我们知道redis是基于单线程多路复用,因此我们不能同时进行内存快照以及响应请求。
而redis因此在持久化时会fork一个子进程,持久化完全由子进程处理,父进程继续处理客户端请求。
子进程只会对数据结构遍历读取然后序列化到磁盘中,但是父进程会持续修改内存数据结构。
因此redis使用了操作系统的COW(copy on write)机制进行数据段page的共享读写。
它把所有数据按每4k一个page来分割,然后子进程就对每一个page去读取然后序列号写到磁盘,而父进程就响应请求,当出现数据改变时,就将对应的page复制一份分离出来然后修改。当然随着修改增多,page会越来越多,内存也会增长,但是worst case只是原有数据内存的两倍;同时redis作为一个缓存,冷数据比例比较高,因此基本上是小部分页面被复制分离。如下图。
RDB的优点:
dump.rdb是个二进制文件,使用RDB恢复非常快
RDB的缺点:
1.dump的时候比较耗费资源
2.只使用RDB恢复的话,基本上会丢失大量数据,因为我们不可能每时每刻都dump备份下来。
什么是AOF
AOF就是redis存储顺序指令序列的log,只记录了内存修改的指令记录。
因此我们可以通过执行aof文件来进行指令重放,以此来恢复数据。
PS:和mysql等数据库不同,redis是先执行了指令,然后再写log(可能是因为和mysql等不同,redis只有执行了这个命令才知道这个命令是否有效)。
突然宕机,AOF日志还没写到磁盘怎么办?
实际上程序对AOF日志写操作时,就是将内容写到kernel为fd分配的一个内存缓存,然后kernel会自动异步将数据刷回磁盘。所以突然宕机,可能日志还没刷到磁盘。怎么办?
linux有个fsync函数,可以强制把fd对应的内容从内核缓存刷到磁盘。但是fsync是个磁盘IO操作,所以会很慢。
所以redis一般会每隔1s执行一次fsync去刷到磁盘。
AOF的优点:
基本上不会数据丢失或很少
AOF的缺点:
1.恢复时是执行日志重放,所以如果日志比较长,就会比较慢。。。
2.fsync是个耗时的IO操作,会增加系统IO负担
混合持久化
没错,大人选择全都要。。
我们选择redis提供的混合持久化模式,rdb快照和增量aof日志同时保存。这里的aof日志就不是全量日志,而是从持久化开始到持久化结束的增量AOF日志,因此这部分AOF日志会很小。
当我们重启redis时,先加载rdb内容,然后再重放增量AOF文件,以此提高效率。