版本:redis - 5.0.4
参考资料:redis设计与实现
文件:src下的dict.c dict.h
一、dict.h
dict(字典),又称为符号表,关联数组或映射。用于保存键值对。
- 字典使用哈希表作为底层实现。
- 使用两张表来进行大小的扩展。
- 用链接的方式处理冲突。新节点头插。
数据结构
//节点:key, v, next
typedef struct dictEntry {
void *key;
union {
void *val;
uint64_t u64;
int64_t s64;
double d;
} v;
struct dictEntry *next;
} dictEntry;
//hash表
typedef struct dictht {
dictEntry **table;//dictEntry数组
unsigned long size;//表大小
unsigned long sizemask;//表的掩码,等于size-1
unsigned long used;//已有的节点数量
} dictht;
//字典
typedef struct dict {
dictType *type;
void *privdata;
dictht ht[2];//两张表
long rehashidx; /* rehashing not in progress if rehashidx == -1 */
unsigned long iterators; /* number of iterators currently running */
} dict;
hash表的存储结构:
rehash(扩展/收缩)
字典中存储了两张hash表,先使用ht[0],rehash时:
- 为ht[1]分配空间:若是扩展,ht[1]的大小等于ht[0].used * 2的第一个2^n;若是收缩,ht[1]的大小等于第一个ht[0].used 的2^n
- 将ht[0]中的全部键值对rehash,存放到ht[1]中。
- 当ht[0]为空时,释放ht[0],将ht[1]设为ht[0],并在ht[1]创建一个空白hash表。
渐进式rehash:当键值对很多时,rehash很慢,甚至需要服务器在一段时间内停止服务,为了避免这样的事情,所以需要渐进式的,分多次rehash到ht[1]。
- 为ht[1]分配空间后,将字典中的rehashidx设为0,说明正在rehash。
- rehash期间,每次对字典增删改查时,除了执行指定操作,还要顺带把ht[0]在rehashidx索引上的全部键值对rehash到ht[1]。完成后,rehashidx加一。
- 当ht[0]为空时,设rehashidx为-1,表示rehash结束。
缺点是,rehash期间同时使用两张表,所以许多操作都需要执行两遍,在ht[0]没有找到的节点在ht[1]上还要再找一次。
操作
//迭代器
typedef struct dictIterator {
dict *d;
long index;
int table, safe;
dictEntry *entry, *nextEntry;
/* unsafe iterator fingerprint for misuse detection. */
long long fingerprint;
} dictIterator;
//参数为 (void *privdata, const dictEntry *de) ,返回值为 void 的函数建立别名 dictScanFunction。
typedef void (dictScanFunction)(void *privdata, const dictEntry *de);
typedef void (dictScanBucketFunction)(void *privdata, dictEntry **bucketref);
/* API */
dict *dictCreate(dictType *type, void *privDataPtr);//生成
int dictExpand(dict *d, unsigned long size);//扩展
int dictAdd(dict *d, void *key, void *val);//添加节点
dictEntry *dictAddRaw(dict *d, void *key, dictEntry **existing);//生成一个空节点,加到dict中
dictEntry *dictAddOrFind(dict *d, void *key);//得到指定节点
int dictReplace(dict *d, void *key, void *val);//替换值
int dictDelete(dict *d, const void *key);//删除给定键的节点
dictEntry *dictUnlink(dict *ht, const void *key);//移除节点 但不释放
void dictFreeUnlinkedEntry(dict *d, dictEntry *he);//释放那个移除的节点
void dictRelease(dict *d);//释放d
dictEntry * dictFind(dict *d, const void *key);//查找
void *dictFetchValue(dict *d, const void *key);//得到值
int dictResize(dict *d);//重新设置大小
dictIterator *dictGetIterator(dict *d);//生成一个不安全的迭代器
dictIterator