前言
所谓快照,就是记录某一瞬间的内存数据,因此,不同于AOF日志记录的是操作命令,RDB文件的内容是二进制数据。因此在Redis恢复数据时,采用RDB的效率会更高,直接将RDB文件读入内存即可,不需要像AOF那样还需要额外执行命令来恢复数据
RDB实现原理
Redis提供了两个命令来生成RDB文件,区别在于是否在主进程里执行:
- save:在主进程里生成RDB文件,由于和执行操作命令在同个进程,所以如果写入时间过长,会阻塞主进程
- bgsave:创建一个子进程来生成RDB文件,避免阻塞主进程
Redis并没有提供专门用于加载RDB文件的命令,是在服务器启动时自动执行RDB文件的加载
不过,如果采用的是bgsave命令,可以通过配置来实现命令自动执行,默认配置如下:
save 900 1
save 300 10
save 60 10000
解释:
- 900秒之内,对数据库进行了至少1次修改,就会执行bgsave
- 300秒之内,对数据库进行了至少10次修改,就会执行bgsave
- 60秒之内,对数据库进行了至少10000次修改,就会执行bgsave
性能分析
注意:Redis的快照是全量快照,即每次执行快照时,都是把内存中的所有数据记录到磁盘中。
可以认为执行快照是一个比较重的操作,如果太频繁可能对Redis产生性能影响;但是如果频率太低,发生故障时,丢失的数据会更多。
通常设置至少5分钟才保存一次RDB快照,这时如果Redis发生宕机,最多丢失5分钟数据
这就是RDB快照的缺点,在发生服务器故障时,丢失的数据会比AOF持久化方式更多,因为RDB快照是采用全量快照的方式,因此执行频率不能太频繁,否则影响Redis性能,而AOF日志可以秒级记录操作命令,相对丢失的数据更少。
bgsave写时复制
执行bgsave过程中,Redis依然可继续处理命令,主进程会通过fork()创建子进程,此时父子进程共享同一片内存数据,页表执行同个物理内存空间。
发生修改内存数据时,物理内存会被复制一份(仅复制被修改的部分,未修改的部分仍共享) 例如,如果主线程(父进程)要修改共享数据里的某一块数据 (比如键值对 A
)时,发生写时复制,于是这块数据的物理内存就会被复制一份(键值对 A'
) ,然后主线程在这个数据副本(键值对 A'
)进行修改操作 ,与此同时,bgsave 子进程可以继续把原来的数据(键值对 A
)写入到 RDB 文件。
写时复制的目的是减少创建子进程的性能损耗,从而加快创建子进程速度,毕竟创建子进程是会阻塞主进程的。
数据不一致
bgsave过程中,如果主进程修改了共享数据,发生写时复制后,RDB快照保存的是原来的内存数据,而主进程刚修改的数据,只能交由下一次的bgsave命令。
如果恰好系统在RDB快照文件创建完毕后宕机,那么Redis将丢失主进程此前在该写快照期间修改的数据
内存占用激增
在Redis执行RDB持久化期间,刚fork时,主进程和子进程共享同一物理内存数据,但途中主进程处理了写操作,修改了共享内存,于是当前被修改数据的物理内存就被复制一份。
极端情况下,如果所有的共享内存都被修改了,此时的内存占用将是原来的2倍。
因此,针对写操作多的场景,要留意下RDB快照记录过程的内存变化,防止OOM。
参考
《小林coding》