Redis 哈希表中负载因子问题

最近在看Redis设计与实现,发现Redis中的哈希表也是根据负载因子的扩容和收缩。

当以下条件中的任意一个被满足时, 程序会自动开始对哈希表执行扩展操作:

服务器目前没有在执行 BGSAVE 命令或者 BGREWRITEAOF 命令, 并且哈希表的负载因子大于等于 1 ;
服务器目前正在执行BGSAVE 命令或者 BGREWRITEAOF 命令, 并且哈希表的负载因子大于等于 5 ;

上面意思是Redis在进行rdb快照备份的时候,负载因子为5。没有执行rdb快照时负载因子为1。

 

为什么负载因子可以大于1,并且达到5?HashMap也就0.75而已。

# 负载因子 = 哈希表已保存节点数量 / 哈希表大小
load_factor = ht[0].used / ht[0].size

因为已保存节点数量是包括冲突的节点数量,所以已保存节点数量是有可能大于哈希表大小的,所以也就可以达到5。
 

为什么在执行 BGSAVE 命令或者 BGREWRITEAOF 命令, 负载因子要大于等于 5?而未执行时大于等于1?

书中的解释是:

根据 BGSAVE 命令或 BGREWRITEAOF 命令是否正在执行, 服务器执行扩展操作所需的负载因子并不相同, 这是因为在执行BGSAVE 命令或BGREWRITEAOF 命令的过程中, Redis 需要创建当前服务器进程的子进程,
而大多数操作系统都采用写时复制(copy-on-write)技术来优化子进程的使用效率, 所以在子进程存在期间,
服务器会提高执行扩展操作所需的负载因子, 从而尽可能地避免在子进程存在期间进行哈希表扩展操作, 这可以避免不必要的内存写入操作,最大限度地节约内存。

 
又又又触及到知识盲区了,什么是写时复制?这就要拿linux系统来讲了。

首先了解什么是fork?

在linux系统中,fork是类Unix操作系统上创建进程的主要方法。fork用于创建子进程(等同于当前进程的副本)。

Copy On Write原理:

fork创建出的子进程,与父进程共享内存空间。也就是说,如果子进程不对内存空间进行写入操作的话,内存空间中的数据并不会复制给子进程,这样创建子进程的速度就很快了!(不用复制,直接引用父进程的物理空间)。

Copy On Write技术实现原理:

fork()之后,kernel(内核)把父进程中所有的内存页的权限都设为read-only,然后子进程的地址空间指向父进程。当父子进程都只读内存时,相安无事。当其中某个进程写内存时,CPU硬件检测到内存页是read-only的,于是触发页异常中断(page-fault),陷入kernel(内核)的一个中断例程。中断例程中,kernel就会把触发的异常的页复制一份,于是父子进程各自持有独立的一份。

Copy On Write技术好处是什么?

COW技术可减少分配和复制大量资源时带来的瞬间延时。

COW技术可减少不必要的资源分配。比如fork进程时,并不是所有的页面都需要复制,父进程的代码段和只读数据段都不被允许修改,所以无需复制。

Copy On Write技术缺点是什么?

如果在fork()之后,父子进程都还需要继续进行写操作,那么会产生大量的分页错误(页异常中断page-fault),这样就得不偿失。

你可能感兴趣的:(redis,java,redis)