关键代码位置:src/dict.h
字典是一种键值对映射,但C语言无这种数据结构,Redis自己实现了一套。另外字典是hash的底层实现之一。
数据结构概述说明:
- ht数组有两个元素,没有rehash情况下,默认用ht[0],而ht[1]只会在rehash即rehashidx=0时才会用到
- 每个ht都会有used标识多少个实际已有节点数量
- 采用拉链法解决hash冲突
其核心结构源码如下:
typedef struct dict {
// 类型处理函数
dictType *type;
// 类型处理函数私有值
void *privdata;
// 两个hash表
dictht ht[2];
// rehash标示,为-1表示不在rehash,不为0表示正在rehash的桶
long rehashidx; /* rehashing not in progress if rehashidx == -1 */
// 当前正在运行的安全迭代器数量
int16_t pauserehash; /* If >0 rehashing is paused (<0 indicates coding error) */
} dict;
typedef struct dictht {
dictEntry **table;// 哈希表数据,table是一个数组
unsigned long size;// 哈希表大小
unsigned long sizemask;// 哈希表大小掩码,用于计算哈希值
unsigned long used;// 该hash表已有节点数量
} dictht;
typedef struct dictEntry {
void *key;// 键
union {// 值
void *val;
uint64_t u64;
int64_t s64;
double d;
} v;
struct dictEntry *next;// 指向下一个hash节点,形成链表
} dictEntry;
下面我们大概说说核心方法的执行方式
0 hash值计算的方式
h = dictHashKey(d, key);// 计算键的哈希值
idx = h & d->ht[table].sizemask;// table等于0或1
he = d->ht[table].table[idx];
1 缩小字典容量(函数:dictResize)
让它的已用节点数和字典大小之间的比率接近 1:1,默认有个最小的值。
2 扩展或创建一个字典(函数:_dictExpand)
1 ht[0]为空,初始化ht[0]。否则初始化ht[1],并设置rehashidx=0标识正在rehash中
3 rehash过程是渐进式(函数:dictRehash)
分为N步迁移,每步以一个桶(一个数组下标)作为单位,然后将这个桶上所有的ht[0]迁移到ht[1]上,并把ht[0].used--、ht[1].used++
4 字典添加键(函数:dictAddRaw)
执行前会看下是否rehashing,如果是dictRehash渐进式rehash。然后再判断是否rehashing中,如果是就添加到ht[1],反之添加到ht[0]
5 字典替换键对应的值(函数:dictReplace)
先调用dictAddRaw添加,如果不存在则添加成功返回1。否则设置新值,释放旧值。
6 字典删除对应的键(函数:dictGenericDelete)
如果是rehashing中,就渐进式rehash。先找ht[0]的节点(找不到就拉链法跳到下一个链表节点找),最后还找不到并且非rehashing中,就终止,否则跑去ht[1]继续找。
7 清除字典上的所有数据(函数:dictRelease)
先清除dict#ht数组的数据,然后再清除dict本身
8 返回字典中对应键的节点(函数:dictFind)
如果是rehashing中,就渐进式rehash。现在ht[0]找,找不到并且是rehashing中就去ht[1]找
其他自己看gitee~~~~~~~~~~~