引言
虽说 Redis 是内存数据库,但是它为数据的持久化提供了两个技术。由这两个技术构成了3种持久化方式:
- RDB快照(snapshotting)
- 只追加文件(append-only file,AOF)
- RDB和AOF的混合持久化(Redis 4.0 新增)
这两种技术都会用各用一个日志文件来记录信息,但是记录的内容是不同的。
- AOF 文件的内容是操作命令;
- RDB 文件的内容是压缩后的二进制数据。
本篇博客我们就来介绍一下Redis持久化策略之RDB快照。
什么是RDB快照?
Redis 可以通过创建快照来获得存储在内存里面的数据在某个时间点上的副本。Redis 创建快照之后,可以对快照进行备份,可以将快照复制到其他服务器从而创建具有相同数据的服务器副本(Redis 主从结构,主要用来提高 Redis 性能),还可以将快照留在原地以便重启服务器的时候使用。快照持久化是 Redis 默认采用的持久化方式,在 redis.conf 配置文件中默认有此下配置:
#在900秒(15分钟)之后,如果至少有1个key发生变化,Redis就会自动触发bgsave命令创建快照。
save 900 1
#在300秒(5分钟)之后,如果至少有10个key发生变化,Redis就会自动触发bgsave命令创建快照。
save 300 10
#在60秒(1分钟)之后,如果至少有10000个key发生变化,Redis就会自动触发bgsave命令创建快照。
save 60 10000
这里配置文件的选项叫save,但是Redis在程序中实际上执行的是bgsave(默认),Redis在真正执行RDB快照保存的时候确实有save和bgsave两种方式,它们的区别在于是否在主线程种执行:
- 执行了
save
命令,就会在主线程生成 RDB 文件,由于和执行操作命令在同一个线程,所以如果写入 RDB 文件的时间太长,会阻塞主线程; - 执行了
bgsave
命令(默认选项),会fork出一个子进程来生成 RDB 文件,这样可以避免主线程的阻塞;
RDB 文件的加载工作是在服务器启动时自动执行的,Redis 并没有提供专门用于加载 RDB 文件的命令。
要注意一点,Redis的快照是全量快照,也就是说执行的时候会将内存中的所有数据全部记录到磁盘中。所以对Redis而言,执行快照是比较"重"的操作,太频繁势必会影响Redis性能,而如果频率太低也可能会导致服务器出故障的时候损失的数据过多。比如我们设置至少5min才保存一次快照,这时如果Redis出现宕机,意味着最多可能丢失5min的数据。
上述也正是Redis的RDB快照持久化的缺点,对比后面我们会将的AOF持久化策略,AOF能以秒级记录,丢失的数据相对就较少了。
RDB的数据修改问题
有这样一个问题,当我们使用bgsave保存快照时,因为是fork出子线程去处理,那么这时候主线程还能接收操作,修改数据吗?
如果不支持修改,那么需要阻塞主线程的操作,性能降低很多。所以在执行bgsave的时候,Redis依然可以继续处理操作命令的。那么这是怎么做到的呢?关键技术就是写时复制技术(Copy On Write,COW)。
执行 bgsave 命令的时候,会通过fork()
创建子进程,此时子进程和父进程是共享同一片内存数据的,因为创建子进程的时候,会复制父进程的页表,但是页表指向的物理内存还是一个。
只有在发生修改内存数据的情况时,物理内存才会被复制一份。
这样的目的是为了减少创建子进程时的性能损耗,从而加快创建子进程的速度,毕竟创建子进程的过程中,是会阻塞主线程的。
所以,创建 bgsave 子进程后,由于共享父进程的所有内存数据,于是就可以直接读取主线程(父进程)里的内存数据,并将数据写入到 RDB 文件。
当主线程(父进程)对这些共享的内存数据也都是只读操作,那么,主线程(父进程)和 bgsave 子进程相互不影响。
但是,如果主线程(父进程)要修改共享数据里的某一块数据(比如键值对 A)时,就会发生写时复制,于是这块数据的物理内存就会被复制一份(键值对 A’),然后主线程在这个数据副本(键值对 A’)进行修改操作。与此同时,bgsave 子进程可以继续把原来的数据(键值对 A)写入到 RDB 文件。
就是这样,Redis 使用 bgsave 对当前内存中的所有数据做快照,这个操作是由 bgsave 子进程在后台完成的,执行时不会阻塞主线程,这就使得主线程同时可以修改数据。
我们可以发现,发生Copy On Write的时候,RDB快照写入的是原始的数据,而主线程刚刚修改的数据,只能交给下一次bgsave来保存了。
简单来说,就是在保存RDB快照期间,无法保存在这期间修改的数据,如果系统恰好在RDB快照创建完崩溃,那么Redis会丢失这期间修改的数据。
另外,写时复制有个极端情况,如果所有内存都被修改,就需要double的内存空间。
总结
本篇博客我们介绍了Redis的持久化策略—RDB快照,下一篇将详解另一个策略AOF,敬请期待。