字典是什么(hashtable)
简单来说就是Redis中hash数据结构的底层实现 当数据小, 并且数量不多的时候会用ziplist来实现hash结构
总体结构
这里先给出大体的结构, 便于理解
dict
字典底层又是由dict实现的, 下图是dict的结构
typedef struct dict{
void *type;
void *privdata;
dictht ht[2];
int trehashidx;
}dict;
dictht[]数组长度为2, 一般我们使用dictht[0], 另外一个dictht[1]作为rehash使用
dictht(散列表)
接下来我们看一下dictht(散列表的实现)
typedef struct dictht
{
dictEntry **table;
unsigned long size;
unsigned long sizemask;
unsigned long used;
}dictht;
dictEntry
- dictEntry是一个散列表节点
散列表的节点是由下定义的
typedef struct dictEntry
{
void *key;
union{
void *val;
uint64_tu64;
int64_ts64;
}v;
struct dictEntry *next;
}dictEntry;
- key就是实际存储键的地方
- 联合体中就是实际存储值的地方
- 散列表节点还有一个next域指向下一个节点, 可以用来解决哈希冲突问题
如何解决哈希冲突
1. 链表法
当有两个或以上的键被分配到散列表数组同一个索引上时,就发生了键冲突。Redis使用链表法解决散列冲突。 值得一提的是比如V0先加入这个节点中, 又来一个V1和V0的值一个都被分配 到了一个地址, 那么就会在V0的头部插入V1.
2.rehash法
随着操作的进行, dict内保存的键值对,会不断的减少或者增加, 我们需要保证负载因子的正常, 那么就要重新进行分配内存 这个时候dicht[1]就可以起到作用了, 并且rehashids也会设置为0表示正在rehash中 rehash的过程 1.为字典的ht[1]散列表分配空间,这个空间的大小取决于要执行的操作以及ht[0]当前包含的键值对数量(即:ht[0].used的属性值)
- 扩展操作:ht[1]的大小为 第一个大于等于ht[0].used*2的2的n次方幂。如:ht[0].used=3则ht[1]的大小为8,ht[0].used=4则ht[1]的大小为8。
- 收缩操作: ht[1]的大小为 第一个大于等于ht[0].used的2的n次方幂。
2.将保存在ht[0]中的键值对重新计算键的散列值和索引值,然后放到ht[1]指定的位置上. 当然这不是一步完成的, 是一部分一部分的赋值过去 3.当我们把ht[0]上面所有的键值对都给移过去到h[1]之后, 就会释放h[0]的空间, 并且将h[1]记为ht[0], 最后再创建一个新的ht[1]散列表为下一次rehash做准备。
这就是本次全部内容,若是看不懂建立配合着图来理解
|