1. Redis中rehash概念
Redis 的字典使用哈希表作为底层实现, 一个哈希表里面可以有多个哈希表节点, 而每个哈希表节点就保存了字典中的一个键值对。
哈希表中的键值对随着不断进行的操作增加或减少,为了将哈希表的负载因子维持在较为合理的范围内,程序需对哈希表的大小进行相应的扩展或者收缩,而rehash(重新散列)操作就可以完成这项工作。
2. 负载因子的概念
Redis中,loader_factor:哈希表中键值对数量 / 哈希表长度。
当hash表中键值对数量很少是,负载因子也会很小。
当负载因子大于1,则说明一定出现了hash冲突,负载因子阅读冲突的键值对越多。
3. 扩展和收缩操作
扩容:当以下条件中的任意一个被满足时, 程序会自动开始对哈希表执行扩展操作:
服务器目前没有在执行 BGSAVE 命令或者 BGREWRITEAOF 命令, 并且哈希表的负载因子大于等于 1 ;
服务器目前正在执行BGSAVE 命令或者 BGREWRITEAOF 命令, 并且哈希表的负载因子大于等于 5 ;
收缩: 当负载因子小于等于0.1时,会执行收缩操作。
4. 为什么HashMap的负载因子和Redis差这么多?
因为Redis是用键值对数量除以长度,而不是已使用位置的数量除以长度。
5. 为什么执行BGSAVE或者BGREWRITEAOF命令负载因子设置为5
根据 BGSAVE 命令或 BGREWRITEAOF 命令是否正在执行, 服务器执行扩展操作所需的负载因子并不相同, 这是因为在执行BGSAVE 命令或BGREWRITEAOF 命令的过程中, Redis 需要创建当前服务器进程的子进程,而大多数操作系统都采用写时复制(copy-on-write)技术来优化子进程的使用效率, 所以在子进程存在期间,服务器会提高执行扩展操作所需的负载因子, 从而尽可能地避免在子进程存在期间进行哈希表扩展操作, 这可以避免不必要的内存写入操作,最大限度地节约内存。
补充:BGSAVE和BGREWRITEAOF命令
BGSAVE 命令执行之后立即返回 OK ,然后 Redis fork 出一个新子进程,原来的 Redis 进程(父进程)继续处理客户端请求,而子进程则负责将数据保存到磁盘,然后退出。
Bgrewriteaof 命令用于异步执行一个 AOF(AppendOnly File) 文件重写操作,同样会fork一个子进程去处理。重写会创建一个当前 AOF 文件的体积优化版本。
即使 Bgrewriteaof 执行失败,也不会有任何数据丢失,因为旧的 AOF 文件在 Bgrewriteaof 成功之前不会被修改。
补充:写时复制
什么是fork?
在linux系统中,fork是类Unix操作系统上创建进程的主要方法。fork用于创建子进程(等同于当前进程的副本)。
写时复制原理:
fork创建出的子进程,与父进程共享内存空间。也就是说,如果子进程不对内存空间进行写入操作的话,内存空间中的数据并不会复制给子进程,这样创建子进程的速度就很快了!(不用复制,直接引用父进程的物理空间)。
写时复制技术实现:
fork()之后,kernel(内核)把父进程中所有的内存页的权限都设为read-only,然后子进程的地址空间指向父进程。当父子进程都只读内存时,相安无事。当其中某个进程写内存时,CPU硬件检测到内存页是read-only的,于是触发页异常中断(page-fault),陷入kernel(内核)的一个中断例程。中断例程中,kernel就会把触发的异常的页复制一份,于是父子进程各自持有独立的一份。
Copy On Write技术好处是什么?
COW技术可减少分配和复制大量资源时带来的瞬间延时。
COW技术可减少不必要的资源分配。比如fork进程时,并不是所有的页面都需要复制,父进程的代码段和只读数据段都不被允许修改,所以无需复制。
Copy On Write技术缺点是什么?
如果在fork()之后,父子进程都还需要继续进行写操作,那么会产生大量的分页错误(页异常中断page-fault),这样就得不偿失。
了解了写时复制的原理,我们现在可以回答:为什么使用写时复制技术创建子进程时进行哈希表扩展会造成不必要的内存写入操作?
首先服务器进程在执行BGSAVE或者BGREWRITEAOF命令时,创建了新的子进程,子进程和父进程共享内存空间。此时如果我们扩展哈希表,那么那么相当于往父进程写入数据,同时会导致子进程进行复制操作,因为复制是按页来进行复制,所以,如果在执行上述命令时大量写入,则会出现大量页中断,导致大量复制操作,影响系统性能。
1760

被折叠的 条评论
为什么被折叠?



