关于redis RDB原理的理解

redis rdb本质

先说它的本质吧,redis rdb是redis持久化的一种业务逻辑手段,通过全量拷贝某个时间节点全部数据生成rdb镜像文件,达到持久化。

redis rdb实现镜像生成的两个方式

  1. save:rdb的实现在redis中有两个不同的路径,一个是直接使用主进程生成镜像,这个是通过save直接调度rdbsave函数,这个操作会阻塞主进程。
  2. 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操作为啥不是多线程,而使用多进程?

  1. 如果使用多线程,那么这里会共享同一个进程的内存,如果需要修改,这里是需要加锁的。
  2. 线程之间的切换,会耗费较多的性能,不满足redis的高性能要求

redis rdb 有多个bgsave请求,为啥只处理其中一个,其他全部忽略?

当redis中已经有一个子进程了,那么这时候提交bgsave请求,是会被忽略的,并不会进入队列累积。因为如果进入累计,那么子进程可以算是一直存在,并且存储这些请求也是会占用资源的。为啥不生成多个子进程去处理呢?这个方式意义不大,因为redis生成rdb镜像的方式是,全量拷贝某个时间节点的数据,生成二进制文件。那么问题来了,当数据量过大的时候,全量拷贝生成的镜像文件的速度其实是比较慢的。如果有多个进程,cpu应该会唱:“听我说,谢谢你。。。。”。并且redis的镜像文件默认是覆盖式的,也就是说rdb文件实际在指定存储镜像文件的目录下有且只有一个,除非做了重命名操作。

关于子进程结束后通知主进程的原因

内存在fork的时候被设置为只读模式了,如果主进程在此期间又被写操作,那么你总得让这些新的数据后面弄进内存空间吧。并且进程之间的状态是不互通的,所以镜像是否生成完毕,也是需要让用户知道的吧。

RDB优缺点

  1. 优点:
    • 压缩后的二进制文件,适用于备份、全量复制及灾难恢复
    • RDB恢复数据性能优于AOF方式
  2. 缺点:
    • 无法做到实时持久化,每次都要创建子进程,频繁操作成本过高
    • 保存后的二进制文件,不同版本直接存在兼容性问题

参考资料

https://blog.csdn.net/qq_26222859/article/details/124027095
https://zhuanlan.zhihu.com/p/187596888

posted @ 2022-07-27 11:00  影梦无痕  阅读(257)  评论(0)    收藏  举报