Redis的RDB是怎么实现的?
作者:程序员马丁
热门项目实战社群,收获国内众多知名公司面试青睐,近千名同学面试成功!助力你在校招或社招上拿个offer。
答题思路
回答话术
当通过 BGSAVE
指令生成 RDB 的时候,Redis 会 fork
出一个子进程,它会基于写时复制机制,在不阻塞主线程的情况下,将此刻的数据库全量数据保存为二进制快照。
具体的来说,在最开始的时候 fork
子进程的时候,操作系统会为其拷贝父进程的内存页,但是此时两者都指向同一块物理内存。当主进程发生写操作时,会真正的将父进程的数据拷贝到独立的物理内存中。此时父子进程的物理内存彼此独立,互不干涉,父进程继续处理增量数据,而子进程则根据拷贝出来的旧数据生成 RDB 快照。
相对 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 文件过程中占用的额外内存是比较低的。