跳至主要內容

RDB 快照

AruNi_Lu数据库Redis约 2120 字大约 7 分钟

本文内容

1. 为什么需要 RDB 快照?

在上一章中讲解了 Redis 的一种持久化方案 — AOF 日志,它记录了操作的命令,在 AOF 文件过大时会进行 AOF 重写来压缩文件;而且提供了三种刷盘策略,来保证数据丢失和阻塞主线程之间的平衡。那为什么还需要 RDB 快照呢?

这就要从 AOF 日志的格式说起,AOF 日志中记录的是 逻辑日志,而 不是实际的数据,所以使用 AOF 进行故障恢复时,需要把日志命令都执行一遍,如果日志很多,Redis 恢复的时间就会很长,影响正常使用。这时候就需要 RDB 快照了。

2. 什么是 RDB 快照?

RDB(Redis DataBase)快照,就是一个 内存快照,可以 把内存中某个时刻的数据记录下来,当然了,也是记录到磁盘上,这样就算宕机,快照文件也不会丢失。

与 AOF 不同,RDB 记录的是某个时刻的数据,而不是命令,所以是 真实的物理数据。在做数据恢复时,可以直接把 RDB 文件读入内存,很快就能完成数据恢复。

因为 Redis 的数据都在内存,在进行 RDB 快照时是 全量快照,也就是会 把内存中的数据全部记录到磁盘中

由于是全量快照,所以当数据量很大时,执行 RDB 快照是非常耗时的,此时肯定不能阻塞主线程,Redis 也提供了两个命令来生成 RDB 文件:

  • save:主线程执行,会导致 Redis 阻塞
  • bgsave:fork 一个 子进程 执行,不会阻塞主线程,这也是默认配置。

疑问:既然全量快照很耗时,那能不能实现增量快照呢?

要实现增量快照,那在这次 RDB 快照生成后,后续就需要记录哪些数据被修改了,而这 需要额外增加元数据信息去记录,会带来不小的开销。而且如果一个键值对本身很小,为了记录修改而需要的额外元数据就会显得占比很大,内存对于 Redis 是非常宝贵的,所以有些得不偿失了,因此 Redis 也并没有实现增量快照的功能。

既然 RDB 快照不是一条条命令,这就意为着不会随每条命令执行时写入文件,那什么时候进行 RDB 快照呢?执行快照时 Redis 还能否处理请求?这些问题都是 RDB 快照需要解决的。

3. 什么时候进行 RDB 快照?

RDB 记录的是某一时刻的数据,所以进行 RDB 快照的时机就非常重要了,生成 RDB 的间隔越小,比如 1 秒生成一次,那数据丢失的就越少。

bgsave 生成 RDB 快照时不会阻塞主线程,这意味着我们可以让 RDB 快照生成的时间间隔越小越好吗?

其实也并不是,虽然 bgsave 不会阻塞主线程,但频繁执行全量快照,会带来下面的问题:

  • 频繁将全量数据写入磁盘,会给磁盘带来很大压力,可能前一个还没做完,后一个又开始了,导致恶性循环;
  • bgsave 需要由主线程 fork 出子进程,与 AOF 重写类似,fork 这个过程会阻塞主线程,数据越多,fork 过程越长。所以 频繁执行 bgsave 会导致频繁 fork 子进程,导致频繁阻塞主线程(所以 Redis 中只能有一个 bgsave 在运行)。

其实,Redis 提供了配置来规定多久执行一次 bgsave,默认提供的配置如下:

save 900 1
save 300 10
save 60 10000

注:虽然配置项叫 save,但之际执行的是 bgsave,并不会阻塞主线程。

这三个配置的含义分别如下:

  • 900 秒内,至少进行了 1 次修改操作;
  • 300 秒内,至少进行了 10 次修改操作;
  • 60 秒内,至少进行了 10000 次修改操作。

只要满足上面条件的任意一个,就会执行 bgsave,对 Redis 进行全量快照。

所以我们可以根据自己业务能接受数据丢失的实际情况,来进行配置。

4. 进行快照时能修改数据吗?

在执行 RDB 快照时,如果数据还在频繁的变动,那肯定会影响快照的完整性,毕竟快照需要消耗挺长时间。

例如,在 t1 时刻执行内存快照,需要 10s 才能做完,在这 10s 内陆陆续续有数据刷入磁盘,而此过程中,比如 t1 + 3s 时刻某些数据发生了修改,即 t1 + 3s 相比与快照开始时刻 t1 来说,数据不同了,就会导致数据的不完整,因为 这些被修改的数据已经不是 t1 时刻的状态了

所以,在执行快照期间,自快照开始时刻起,内存中的数据就不能被修改了。那这就意味着快照期间 Redis 不能处理写请求了吗?

很显然 Redis 不会这么做,因为 RDB 快照还是挺耗时的,如果不允许执行写请求,那将大大降低 Redis 的性能。

看到这里,有没有觉得这个场景和 AOF 重写open in new window 时很像?AOF 重写也是需要根据当前时刻的数据,进行全量重写。那 AOF 重写时能进行写操作,执行 RDB 快照时为什么就不行?

其实,这里采用的技术与 AOF 重写相同,都是 写时复制(Copy-On-Write,COW)技术

具体来说,bgsave 执行时,主线程会 fork 出一个子进程,这个过程也是只会复制页表,通过页表达到共享内存的效果,bgsave 子进程此时就可以读取内存中的数据,写入 RDB 文件。当主线程发生写操作时,就发生了 COW,此时主线程会真正拷贝出一块物理内存,在这块新的物理内存上进行修改操作,而 bgsave 子进程读取的还是原来的内存,不会影响 RDB 的生成

图示如下所示:

img

通过 COW,既保证了快照的完整性,也允许主线程对数据进行修改

当然了,在快照过程中修改后的数据,是不在 RDB 文件中的,也就是说 会丢失最新数据

5. RDB 和 AOF 的优劣

学习完 RDB 后,可以发现 RDB 有自己的优势,当然也有劣势,下面就来看看 RDB 和 AOF 的优劣对比。

数据恢复的速度 上看:

  • RDB 快照 记录的是实际真实的 物理数据,恢复时可以直接读取到内存,速度速度 很快
  • AOF 日志 记录的是命令、逻辑日志,恢复时还需要执行命令,恢复速度 较慢

数据丢失情况 上看:

  • RDB 快照丢失数据的情况 和快照的频率有关,执行快照的频率越快,丢失的数据就越少,但是频率并不好控制,频率太快也会导致写磁盘、fork 子进程带来的性能消耗;
  • AOF 日志提供了 三个刷盘时机参数 来平衡数据丢失和性能的平衡,当刷盘时机为 Always 时,基本不会出现丢失数据的情况。

可以看出,RDB 快照的最大优势是恢复速度快,但快照的频率不好控制;AOF 快照的最大优势是丢失数据少,但恢复速度较慢

那有什么方法既能利用 RDB 的快速恢复优势,又能以较小的开销做到尽量少丢数据呢?答案是 Yes,Redis 4.0 中提出的 混合持久化 就可以实现,我们下篇文章见。

6. 参考文章

  • 《Redis 核心技术与实战》
上次编辑于: