前言
今天在看《Redis设计与实现》,讲解字典的实现时,说道Redis在执行BGSAVE和BGREWRITEAOF命令时,
哈希表的负载因子大于等于5,而未执行这两个命令时大于等于1。
而解释仅仅是提了一句原因是:
在执行BGSAVE和BGREWRITEAOF命令时,Redis需要创建当前服务器进程的子进程,而大多数操作系统都采用写时
复制技术来由于子进程的使用效率,所以在子进程存在期间,服务器会提高执行扩展操作所需的负载因子,从而尽可
能避免在子进程存在期间进行哈希表扩展操作,这可以避免不必要的内存写入操作。
哈希表的扩展因子:哈希表已保存节点数量/哈希表大小。扩展因子决定了是否扩展哈希表。
因为基础薄弱,所以对这个结论产生了很多疑问:
- 什么是写时复制?
- 为什么使用写时复制技术创建子进程时进行哈希表扩展会造成不必要的内存写入操作?
写时复制
要了解什么是写时复制,我们还需要知道操作系统是怎么创建子进程的。我们一linux操作系统进行讲解。
创建子进程的唯一方式是调用fork函数,这个fork函数为创建一个和父进程几乎完全相同的进程。创建子进程时,需要将父进程的除了正文段
之外几乎所有的数据拷贝至子进程,如堆,栈,数据段。
创建子进程拷贝几乎所有父进程的数据会导致创建子进程的过程很慢,从而有了写时复制这种技术来提高创建子进程的过程。从下面参考的
博客中的知识我认识到:
在不使用写时复制技术的情况下,我们为进程创建一个子进程时会导致子进程拷贝父进程的数据段,堆,栈,仅有
正文段不会被拷贝。
而在使用写时复制技术的情况下,我们为进程创建一个子进程时不会拷贝任何数据,此时父进程和自己成共享同一份数据,
仅当父进程或者自己成需要对这份数据进行写入时,才为子进程分配相应的物理空间。
为什么使用写时复制技术创建子进程时进行哈希表扩展会造成不必要的内存写入操作?
了解了写时复制之后我们就能回答这个问题了。首先服务器进程在执行BGSAVE或者BGREWRITEAOF命令时,创建了新的子进程;此时如果我们扩展哈希表,那么那么相当于忘父进程写入数据,同时会导致子进程进行复制操作。
参考:
Linux写时拷贝技术(copy-on-write)