Redis哈希的实现比较难理解,为了提升性能,采用分治的思想。当哈希桶需要扩容的时候,并不是直接全部更新,而是定义两个哈希表,新表旧表。当处于扩容状态时,每进行一次插入或查找操作就进行一次更新操作,即把原始表的一个数据移到新的表中。当所有数据迁移完成后,新表变成旧表,旧表变成新表。这样在扩容的过程就变成了分步进行。减少了因扩容而产生的长时间等待。具体的实现,代码见,仔细看了代码后,了解会深很多。
先来看看dict的数据结构
数据结构
typedef struct dict { //哈希的数据结构
dictType *type; //dictType 其实是hash的一些函数操作,见dictType详细描述
void *privdata; //私有数据。
dictht ht[2]; //前面所说的两个表,ht[0] 为旧表 ht[1]为新表
long rehashidx; /* rehashing not in progress if rehashidx == -1 */ //不处于扩容状态是为-1 其余时刻为扩容进行到的下标。
unsigned long iterators; /* number of iterators currently running */ //迭代器的数量
} dict;
typedef struct dictType { //type 用户自定义的一些函数
uint64_t (*hashFunction)(const void *key); //hash函数
void *(*keyDup)(void *privdata, const void *key);//key复制
void *(*valDup)(void *privdata, const void *obj);//val复制函数
int (*keyCompare)(void *privdata, const void *key1, const void *key2);//两个key值比较
void (*keyDestructor)(void *privdata, void *key);//key的析构函数
void (*valDestructor)(void *privdata, void *obj);//val的析构函数
} dictType;
typedef struct dictht { //每一张表的数据结构
dictEntry **table; //使用的是拉链法。
unsigned long size;//哈希表的大小
unsigned long sizemask;//长度-1 为了方便获取元素的index
unsigned long used;//哈希表已经使用的大小
} dictht;
typedef struct dictEntry { //每一个hash元素的结构
void *key;
union { //联合体存储double int uint 或者是别的数据的指针
void *val;
uint64_t u64;
int64_t s64;
double d;
} v;
struct dictEntry *next; //拉链法中的下一个节点
} dictEntry;
typedef struct dictIterator { //dict的迭代器
dict *d; //迭代器对应的表
long index;//table中的下标,标记table[index]
int table, safe;//table 判断是表th[0] 还是th[1] safe 判断是是否为安全
dictEntry *entry, *nextEntry;//当前拉链的元素与下一个元素
/* unsafe iterator fingerprint for misuse detection. */
long long fingerprint;//不安全的迭代器,即正在更新的中的表的迭代器,使用指纹来标记。
} dictIterator;
整个dict的图,一样是垃圾字体。
看完了数据结构,让我们来看看源代码实现吧。挑了一些重要的来看。
dictCreate dict创建
dict *dictCreate(dictType *type,
void *privDataPtr)
{
dict *d = zmalloc(sizeof(*d)); //机智的使用*d 则sizeof 为d的类型,不是指针的大小。
_dictInit(d,type,privDataPtr);//利用该函数对新建的hash表进行初始化。
return d;
}
_dictInit 接上面的初始化
int _dictInit(dict *d, dictType *type,
void *privDataPtr)
{
_dictReset(&d->ht[0]); //初试化两个表,详见下面的函数,下面都是简单的赋值。
_dictReset(&d->ht[1]);
d->type = type;
d->privdata = privDataPtr;
d->rehashidx = -1;
d->iterators = 0;
return DICT_OK;
}
static void _dictReset(dictht *ht)
{
ht->table = NULL;
ht->size = 0;
ht->sizemask = 0;
ht->used = 0;
}
dictResize 扩容入口,接下来就是艰难的开始了,下面是一些的重要实现方法。
/* R