关于redis RDB原理的理解
redis rdb本质
先说它的本质吧,redis rdb是redis持久化的一种业务逻辑手段,通过全量拷贝某个时间节点全部数据生成rdb镜像文件,达到持久化。
redis rdb实现镜像生成的两个方式
- save:rdb的实现在redis中有两个不同的路径,一个是直接使用主进程生成镜像,这个是通过save直接调度rdbsave函数,这个操作会阻塞主进程。
- bgsave:而另一个是通过linux的api从主进程中生成一个子进程(fork),然后通过这个子进程去做镜像保存,这个是通过生成的子进程调度rdbsave,这个操作不会阻塞主进程,且子进程只用作镜像生成。这个子进程在redis中有且只有一个,如果当前已经存在,那么会忽略后面的bgsave请求。我们重点讨论bgsave实现原理,子进程是如何在内存变化的时候进行镜像持久化的。
redis rdb bgsave实现原理
先看图

这个图我们可以大概的看出,当主进程fork一个子进程后,主进程的内存空间A会被设置为read-only模式,如果这时候有写操作,CPU硬件检测到内存页是read-only的,于是触发页异常中断(page-fault),陷入内核的一个中断例程。中断例程中,内核就会把触发的异常的页复制一份(这里仅仅复制异常页,也就是所修改的那个数据页,而不是内存中的全部数据),于是主子进程各自持有独立的一份。所以主进程的内存更改和子进程的内存其实是互不可见的。所以也就有了主进程这边可以正常接收客户端信息,子进程保存的镜像数据也不会受到影响。当子进程已经存在,如果这时候出现其他的bgsave请求,那么会被忽略。如果镜像生成完成,子进程会通知主进程
总结一句话就是,主进程和子进程直接的内存分为多个页,只有被修改的页被拷贝给主进程,子进程依然使用原本的,没变的内存页。这个操作被称为copyonwrite(写时拷贝)操作。
redis rdb bgsave操作为啥不是多线程,而使用多进程?
- 如果使用多线程,那么这里会共享同一个进程的内存,如果需要修改,这里是需要加锁的。
- 线程之间的切换,会耗费较多的性能,不满足redis的高性能要求
redis rdb 有多个bgsave请求,为啥只处理其中一个,其他全部忽略?
当redis中已经有一个子进程了,那么这时候提交bgsave请求,是会被忽略的,并不会进入队列累积。因为如果进入累计,那么子进程可以算是一直存在,并且存储这些请求也是会占用资源的。为啥不生成多个子进程去处理呢?这个方式意义不大,因为redis生成rdb镜像的方式是,全量拷贝某个时间节点的数据,生成二进制文件。那么问题来了,当数据量过大的时候,全量拷贝生成的镜像文件的速度其实是比较慢的。如果有多个进程,cpu应该会唱:“听我说,谢谢你。。。。”。并且redis的镜像文件默认是覆盖式的,也就是说rdb文件实际在指定存储镜像文件的目录下有且只有一个,除非做了重命名操作。
关于子进程结束后通知主进程的原因
内存在fork的时候被设置为只读模式了,如果主进程在此期间又被写操作,那么你总得让这些新的数据后面弄进内存空间吧。并且进程之间的状态是不互通的,所以镜像是否生成完毕,也是需要让用户知道的吧。
RDB优缺点
- 优点:
- 压缩后的二进制文件,适用于备份、全量复制及灾难恢复
- RDB恢复数据性能优于AOF方式
- 缺点:
- 无法做到实时持久化,每次都要创建子进程,频繁操作成本过高
- 保存后的二进制文件,不同版本直接存在兼容性问题
参考资料
https://blog.csdn.net/qq_26222859/article/details/124027095
https://zhuanlan.zhihu.com/p/187596888

浙公网安备 33010602011771号