字典
字典,又称关联数组或映射,是一种用于保存键值对的抽象数据结构。在字典中,一个键key和一个值value进行关联;字典中的每个键都是独一无二的。
字典的实现
redis的字典使用哈希表作为底层实现,一个哈希表可以有很多节点,而每个节点就保存了字典中的一个键值对。
哈希表
定义结构如下:
typedef struct dictht {
//哈希表数组
dictEntry **table;
//哈希表大小
unsigned long size;
//哈希表大小掩码,用于计算索引值
unsigned long sizemask
//该哈希表已有的节点数量
unsigned long used;
}dictht;
结构示意图如下:
哈希表节点
哈希表节点使用dictEntry结构表示,每个节点都保存着一个键值对:
typedef struct dictEntry {
//键
void *key;
//值,可以是一个指针,uint64_t整数,int64_t整数
union {
void *val;
uint64_t u64;
int64_t s64;
}v;
//指向下一个哈希表节点,形成链表
struct dictEntry *next;
}dictEntry;
结构图如下:
字典
redis中的字典结构如下:
typedef struct dict {
//类型特定函数
dictType *type;
//私有数据
void *privdata;
//哈希表
dictht ht[2];
//rehash索引,不进行rehash时,值为-1
int trehashidx;
}
- [ ] type属性是一个指向dictType结构的指针,每个dictType保存了一组用于操作特定类型键值对的函数,redis会为用途不同的字典设置不同类型特定函数。
- [ ] privdata属性,则保存了需要传递给那些类型特定函数的可选参数。
typedef struct dicType {
//计算哈希值得函数
unsigned int (*hashFunction) (const void *key);
//复制键的函数
void *(*keyDup) (void * privdata,const void *key);
//复制值得函数
void *(*valDup) (void *privdata,const void *obj);
//对比键的函数
int (*keyCompare) (void *privdata,const void *key1,const void key2)
//销毁键的函数
int (*keyDestructor) (void *privdata,void *key);
//销毁值得函数
void (*valDestructor) (void *privdata,void *obj);
}
普通状态下的字典如图所示:
ht是包含两个项的数组,每个项都是一个哈希表,字典只用ht[0]哈希表,ht[1]哈希表只会在对ht[0]哈希表进行rehash时使用。当两个及以上的键值对分配到了哈希数组中的同一个索引上,成为冲突,通过单链表的形式解决
哈希算法
当要将一个新的键值对添加到字典里时,程序需要先根据键值对的键计算出哈希值和索引值,然后再根据索引值,将包含新键值对的哈希表节点放到哈希表数组的指定索引上面。
redis使用MurmurHah2算法来计算哈希值。
新节点总是在已有节点的前边。
rehash
随着操作的不断执行,哈希表的键值对会增加或者减少,为了让负载因子维持在合理的范围内,哈希表需要扩展或者收缩。
步骤如下:
- 为字典的ht[1]哈希表分配空间,这个空间取决于要执行的操作,以及ht[0]当前包含的键值对数量。
- 如果是执行扩展操作,那么ht[1]的大小为:
ht [0].used*2^{n} + 1
- 如果执行的是收缩操作,那么ht[1]的大小为:
ht [0].used*2^{n}
- 将保存在ht[0]中ed所有键值对rehash到ht[1]中,rehash指的是重新计算哈希值和索引,然后将键值对放到哈希表指定的位置上。
- 当ht[0]迁移完之后,释放ht[0],将ht[1]的值设为ht[0],并在ht[1]上创建一个空的哈希表,为下一次rehash做准备。
渐进式rehash
rehash操作不是一次性,集中式的完成,而是分多次渐进式的完成的。在rehash期间,每对字典的添加,删除,查找或者更新操作时,都会进行rehash操作;在渐进式rehash期间,新添加的键值对一律会添加到ht[1]中,而不对ht[0]进行操作。
字典api
函数 | 作用 | 时间复杂度 |
---|---|---|
dictCreate | 创建一个新的字典 | O(1) |
dictAdd | 将给定的键值对添加到字典中 | O(1) |
dictReplace | 将给定的键值对添加到字典中,如果已经存在,那么用新的值取代原有的值。 | O(1) |
dictFetchValue | 返回给定键的值 | O(1) |
dictGetRandomKey | 从字典中随机返回一个键值对 | O(1) |
dictDelete | 从字典中删除给定键所对应的键值对 | O(1) |
dictRelease | 释放给定字典,以及字典中包含的所有键值对 | O(N) N为字典包含的键值对数量 |