Redis的字典使用哈希表做为底层实现,一个哈希表中有多个哈希节点。而每个哈希表节点保存了字典中的一个键值对。
字典结构:
typedef struct dict{
dictType *type;
void *privdata;
dictht ht[2]; //typedef struct dict{
dictType *type;
void *privdata;
dictht ht[2]; //哈希表
int rehashidx //扩容的时候的索引indexID
}
rehash的过程:
上面介绍了在一个字典里包含了ht[0]和ht[1]这两个哈希表。如果要是执行扩容操作,那么先获取下ht[0].used*2到2的n次方。如果要是缩容,则直接是ht[0].used的(2的N次方)即可,当把所有的数据都迁移到了ht[1]后,删除掉ht[0],并且rename ht[1]为ht[0],且再创建一个ht[1]为下次哈希做准备。
rehash满足什么条件才能触发:
(1)服务器当前没有进行bgsave或者bgrewrite aof 命令并且负载因子大于等于1(ht[0].used/ht[0].size = load_factor)
(2) 服务器正在执行bgsave或bgrewriteaof 并且负载因子大于等于5
之所以执行命令和不执行命令的负载因子不同,是因为执行bgsave时候,redis 需要创建子进程,大多数的操作系统都采用写时复制技术来优化子进程的执行效率,所以在执行命令期间,会提高负载因子,尽量避免期间的hash操作,避免不必要的内存写入节约内存。
当hash表的负载因子小于0.1的时候,会执行收缩。
渐进式hash:
是指在rehash的过程中,不会一次性把ht[0]的数据复制到ht[1],这样会对服务器性能造成影响。甚至会停止一段时间的 服务。因此采用分多次、渐进式完成。
总结:
1、Redis中字典使用哈希表作为底层实现,每个字典带两个哈希表,一个用来使用,一个用来rehash
2、rehash的时候不会一次性完成,而是渐进式。