dict
hash通过dict实现,可dict不只是在hash中使用,过期时间、zset的score与value都是dict实现的
dict 结构内部包含两个 hashtable,通常情况下只有一个 hashtable 是有值的。但是在 dict 扩容缩容时,需要分配新的 hashtable,然后进行渐进式搬迁,这时候两个 hashtable 存储的分别是旧的 hashtable 和新的 hashtable。待搬迁结束后,旧的 hashtable 被删除,新的 hashtable 取而代之。
typedef struct dict{
//类型特定函数
dictType *type;
//私有数据
void *privdata;
//哈希表
dictht ht[2];
//rehash索引
//当rehash不在进行时,值为-1
int rehashidx;
} dict;
type和privdata属性是针对不同类型的键值对,为丰富键值对的使用场景而设置的。
- type属性是一个指向dictType的结构指针,每个dictType结构保存了一簇用于操作特定类型键值对的函数,Redis为用途不同的字典设置不同类型特定函数。
typedef struct dictType{
//计算哈希值的函数
unsigned int (*hashFunction)(const void *key);
//复制键的函数
void *(*keyDup)(void *privdata,const void *key)
...
}
- privdata属性保存了需要传给那些类型特定函数的可选参数
hashtable
struct dictEntry {
void* key;
//值
union{
void *val;
uint64_t u64;
int64_t s64;
} v;
dictEntry* next; // next是指向另一个哈希节点的指针,可将多个哈希值相同的键值对连接在一起,以此来解决冲突。
}
struct dictht {
dictEntry** table; // 二维
long size; // 第一维数组的长度
long used; // hash 表中的元素个数
//哈希表大小掩码,用于计算索引值
//总是等于size-1
unsigned long sizemask;
}
Redis计算哈希值方法: hash=dict->type->hashFunction(key); 计算索引值的方法:index=hash & dict->ht[x].sizemask;
扩容
- 为字典ht[1]哈希表分配空间,大小取决于要执行的操作与ht[0]当前键值对的数量。
- 将保存在ht[0]中的所有键值对存放到ht[1]指定的位置。
- 当ht[0]的所有键值对都迁移完毕后,释放ht[0],并指向ht[1],并在ht[1]上创建一个空的哈希表,为下次rehash准备。
- 服务器目前没有在执行BGSAVE命令或BGREWRITEAOF命令,并且哈希表的负载因子>=1。
- 服务器正在执行BGSAVE命令或BGREWRITEAOF命令,并且哈希表的负载因子>=5。
渐进式rehash的过程
在rehash的期间,字典同时使用ht[0],ht[1]两个哈希表。对哈希表的操作会在两个表上进行,比如查找键时,先在ht[0]里面查找,如果为空,就继续到ht[1]里查找。在此期间,新增的键值对都会被添加到ht[1]中,ht[0]不承担任何添加操作,保证ht[0]中的键值对只能是越来越少。
文章末尾请带上以下文字及链接:本文正在参与「掘金小册免费学啦!」活动, 点击查看活动详情