dict ,也叫字典(和python的命名一样),对于java 程序员,可能更熟悉称为hashmap 。 redis的dict 和 Go的map 类似。 底层双table+linkedlist。存在rehash操作。
数据结构
底层hashtable的数据结构
typedef struct dictht {
tableEntry ** table;//底层实际存储值的数组。存储的是tableEntry结构体的指针
unsigned long size;//数组长度,始终为2的n次方
unsigned long sizemark; //hash表大小掩码,用于计算索引值,大小始终为size-1.
unsigned long used;//已有的节点数量。
}
而 tableEntry的结构定义如下
typedef struct tableEntry{
void * key;
union {
void * val;
unit64_t u64;
int64_t s64;
}v; //节点值为一个联合结构体.
struct tableEntry * next;//hash冲突的时候,组成的链表.因为只有next,为了快速 查询,新的冲突节点始终在头部
}
如何确认联合结构体使用的是哪一个字段?(我也没明白)
dict数据结构
typedef stuct dict {
dictht ht[2];//hashtable表
dictType *type;//类型特定函数
void * privdata;//私有数据
int rehashidx ;//未进行`rehash`的时候`rehash`为`-1`
}
type是为了实现多态函数.针对不同数据类型的函数提供不同的功能.dictTyp内部包含了很多的typedef stuct dictType{ unsigned int (*hashFun)(const void * key)//hash函数 void (*keyDup)(void * privdata,const void * key);//复制key的函数 void (*valDup)(void * privdata,const void * obj);//复制值的函数 int (*keyCompare)(void * privdata,const void * key1,const * key2);//key比较函数 void (*keyDestructor)(void * privdata,void *key);//key销毁函数 void (*valueDestructor)(void * privdata,void *value);//key销毁函数 }privdata为传递给type函数的特定值.rehash记录当前的hash进度.-1表示为rehash
hash 值计算
hash = dict->type->hashFun(key)
hashIdx = hash & dict->ht[x]->marksize
redis使用的hash函数为MurmurHash2
渐进式rehash
- 负载因子:
used/size已有的节点数/(dictht.table的size) reindex实际上指的就是内部table表的索引. 在reIndex!=-1的时候,表示开始rehash.此时redis在对dict执行任何操作的时候,都会对table[reIndex]上面的节点进行迁移.然后reIndex+1.直到reIndex = table.siz-1.
rehash过程
- 计算是否超过指定的负载因子(分为
bgsave和非bgsave两种情况,前者为1,后者为5).如果超过.执行rehash - 判断当前使用的
ht大小是否超过特定的值.如果没有.一次rehash结束.否则采用渐进式rehash - 根据当前的
hashtable大小来创建新ht的大小.一般是两倍 - 从
ht[0]的table数组的0位置开始.重新计算hashIdx,存放到ht[1]中.当rehash单次执行次数到了,记录下一个需要rehash的下表到rehash中. - 当
ht[0]中所有的节点都迁移结束后,rehash=-1,是否ht[0]的空间.将ht[0]执行ht[1],并创建一个新的dictht给ht[1](底层table为null).
在执行rehash期间,新增key只能存放到新的dictht,既ht[1].如果在rehash的时候发现ht[1]已经存在相同的key时.释放掉老key. 查询的时候,先查询ht[1],在查询ht[0].两者都没有的时候就没有.
tips
rehash分为扩展和收缩.rehash的效率问题.会不会阻塞请求?渐进式hash.每次请求的时候执行一次reshash操作。redis处于不同的情况下,对于是否rehash的负载因子值的要求不同. 低负载的时候大于1. 高负载的情况下大于5