Skip to main content

Redis的RDB是怎么实现的?

作者:程序员马丁

在线博客:https://nageoffer.com

note

Ragent AI —— 从 0 到 1 纯手工打造企业级 Agentic RAG,拒绝 Demo 玩具!AI 时代,助你拿个offer。

答题思路​

回答话术​

当通过 BGSAVE 指令生成 RDB 的时候,Redis 会 fork 出一个子进程,它会基于写时复制机制,在不阻塞主线程的情况下,将此刻的数据库全量数据保存为二进制快照。

具体的来说,在最开始的时候 fork 子进程的时候,操作系统会为其拷贝父进程的内存页,但是此时两者都指向同一块物理内存。当主进程发生写操作时,会真正的将父进程的数据拷贝到独立的物理内存中。此时父子进程的物理内存彼此独立,互不干涉,父进程继续处理增量数据,而子进程则根据拷贝出来的旧数据生成 RDB 快照。

image.png

相对 AOF,RDB 生成的二进制文件更小,数据恢复起来更快,并且整个流程中完全不会阻塞主进程,但是对应的,由于写时复制,在最坏的情况下可能会占用双倍内存,并且无法保存增量数据。

问题详解​

1. 如何生成 RDB 文件​

我们可以通过 SAVE 命令在主进程中阻塞的生成 RDB 文件,或者通过 BGSAVE 命令指定在子进程中生成 RDB 文件。

此外,也可以在数据库中通过 save 选项,指定当在一定的时间范围内执行了多少次修改时生成 RDB 文件。比如 save 120 10000 表示当在 120 秒内发生了 10000 次修改时,就通过 BGSAVE 在后台生成一分 RDB 文件。

关于数据是如何保存在 RDB 文件中的,请参考文章:Redis RDB sourl.cn/mz7P6X

2. 实现原理​

当 Redis 在 fork 出一个子进程去生成 RDB 文件时,主进程依然还在不同的接受指令并操作数据。与 AOF 不同,由于 RDB 是快照,因此实际上它不会同步增量数据,不过也不会影响父进程的操作。Redis 通过写时复制机制实现这样的效果。

简单的来说,当 fork 子进程后,虽然子进程会从父进程中拷贝内存页,但是实际上的内存页依然还是指向了原本的物理内存。而当父进程修改了内存后,才会真正在物理内存中复制一遍父进程的数据,并让子进程的内存页指向这块物理内存,这就是写时复制。

根据这个原理,当子进程在生成 RDB 文件时,若父进程发生写操作,由于写时复制,子进程会得到一份父进程内存页的拷贝。此时子进程读取的是此时父进程内存数据的拷贝,而父进程继续操作他原本的那份内存,两者互不干涉。

我们都知道,在 Linux 系统中内存页一页是 4KB,由于是以内存页为单位的复制,所以在这个过程中,只要不是所有内存页上的数据都发生了修改,那么生成 RDB 文件过程中占用的额外内存是比较低的。

3. 与 AOF 的区别​

RDB 即 Redis Database,它本质上就是某个时刻的全量数据快照。它以二进制的方式保存了某个时刻 Redis 数据库中的全部数据,Redis 启动后只需要将其加载进内存即可恢复数据。

相比起 AOF,它的优点是:

  • 文件更小:由于 RDB 里面存放的是非常紧凑的二进制数据,因此相比起 AOF 文件,它占用的空间更小,因此也更适合用来频繁的进行全量备份。
  • 加载更快:Redis 只需要将 RDB 加载进内存即可恢复数据,相比起还需要重放指令的 AOF,它要快得多。
  • 不阻塞主进程:生成 RDB 的过程可以完全在子进程中完成,因而不需要在主线程进行任何 IO 操作。

对应的,它的缺点是:

  • RDB 需要 fork 出子进程去完成这个任务,当比较频繁的生成 RDB 文件时,会对 CPU 带来比较大的压力;
  • 无法保存增量数据。