回答重点
默认情况下 Redis 生成 RDB 的过程是异步的(采用 bgsave),主线程会调用 fork 创建一个子线程,由子线程负责将内存的数据写入磁盘,生成 RDB 文件。
那生成 RDB 文件的时候,数据可以修改吗?
当然可以。主线程会正常处理客户端的请求,进行数据的修改。但数据被修改还叫快照吗?
此时就运用了写时复制的技术。
当父进程 fork 出一个子进程后,并不会把父进程的所有内存数据重新复制一份给子进程,而是让主进程和子进程共享相同的内存页面。
底层的实现仅仅复制了页表,但映射的物理内存还是同一个。这样做可以加快 fork 的速度,减少性能损耗(fork会阻塞主线程)。
此时,父进程收到写命令,需要修改数据,那么父进程会将对应数据所在的页复制一份,对复制的副本进行修改。此时子进程指向的还是老的页,因此数据没有变化,符合快照的概念。
通过在写的时候才触发内存的复制,可以显著地降低 Redis 实例的性能压力,最大限度的减少 RDB 对服务正常运行的影响。
扩展知识
如果 RDB 时间长,且写并发高,因为写时复制机制,如果共享的每一页内存都被修改,会使得内存极速膨胀,最大内存可以膨胀两倍,所以要注意内存的使用量,防止内存过载。
RDB 会产生大量的磁盘 I/O,要注意磁盘性能导致的影响。
还需要注意 CPU 负载,毕竟有大量的数据需要写入。
因此如果 RDB 在高峰期可能会影响到正常业务,需要合理安排生成 RDB 的时机。