redis源码分析 - 字典哈希

Redis中的字典使用哈希表作为底层实现,通过结构体dictht和dictEntry来存储键值对。字典的哈希计算涉及到计算哈希值和索引值,解决键冲突采用链地址法。在哈希表负载因子超出一定范围时,Redis进行渐进式rehash,以避免一次性rehash带来的性能影响。此外,字典提供了查找、添加、删除和更新等操作。
摘要由CSDN通过智能技术生成

字典,又称为符号表 (symbol table)、关联数组 (associated array)或映射 (map),是一种用于保存键值对 (key-value pair)的抽象数据结构。在 redis 中,哈希键和数据库都是通过字典作为底层实现的。

结构体

redis 中字典的结构如下:

typedef struct dictEntry {
    void *key;  //键
    union {
        void *val;
        uint64_t u64;
        int64_t s64;
        double d;
    } v;    //值,使用联合体,可以是指针,也可以是其他类型的值
    struct dictEntry *next; //使用拉链法解决哈希冲突问题
} dictEntry;

对两个64位类型解释如下

typedef unsigned __int64 uint64_t;
typedef signed __int64 int64_t;

可以将 __int64理解成 long long

typedef struct dictType {
    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);
    void (*keyDestructor)(void *privdata, void *key);
    void (*valDestructor)(void *privdata, void *obj);
} dictType;

dictType结构体为一簇操作特定类型键值对的函数,privdata为传给这些函数的可选参数

/* This is our hash table structure. Every dictionary has two of this as we
 * implement incremental rehashing, for the old to the new table. */
typedef struct dictht {
    dictEntry **table;
    unsigned long size;
    unsigned long sizemask;
    unsigned long used;
} dictht;

dictht 为哈希表结构,哈希表结构有一个 dictEntry 的双重指针(可理解成 dictEntry 指针数组),size 为哈希表大小,sizemask 为哈希表大小掩码,用于计算哈希索引值,总是等于 size-1,used 为哈希表元素个数,即已有的节点的数量。

typedef struct dict {
    dictType *type;
    void *privdata;
    dictht ht[2];
    long rehashidx; /* rehashing not in progress if rehashidx == -1 */
    int iterators; /* number of iterators currently running */
} dict;

dict 为字典结构,包括两个哈希表ht[2], 一个用于正常使用,另一个,当需要扩大哈希表时,将ht[0]中的节点rehashed 到 ht[1] 上,然后再将 ht[1] 设置为ht[0], ht[1]置空,准备下一次 rehashed。 rehashidx 记录了 rehash 的进度,当没有做 rehash 时,它的值为 -1, 当在做 rehash 的值时,它的值表示的是当前 rehash 到了 ht[0] 中的哪一个位置了(可以理解成 ht[0].table[rehashidx])。 redis 的 rehash 是渐进式的,即通过 rehashidx 一个一个递增的形式 rehash 的。

字典的详细实现

计算哈希值和索引值

redis计算哈希值和索引值,是根据键值来计算的,先计算出哈希值,然后根据哈希值和 sizemask 计算索引值。

/* Returns the index of a free slot that can be populated with
 * a hash entry for the given 'key'.
 * If the key already exists, -1 is returned.
 *
 * Note that if we are in the process of rehashing the hash table, the
 * index is always returned in the context of the second (new) hash table. */
static int _dictKeyIndex(dict *d, const void *key)
{
    unsigned int h, idx, table;
    dictEntry *he;

    /* Expand the hash table if needed */
    if (_dictExpandIfNeeded(d) == DICT_ERR)     //判断是否需要扩大哈希表的大小,如果正在 rehash,返回 true
                                                //如果used/size 大于安全阀值5时,将需要扩大哈希表大小
        return -1;
    /* Compute the key hash value */
    h = dictHashKey(d, key);        //计算哈希值
    for (table = 0; table <= 1; table++) {
        idx = h & d->ht[table].sizemask;    //计算索引值,在哈希表中查找是否存在,存在返回-1,不存在,返回索引值
        /* Search if this slot does not already contain the given key */
        he = d->ht[table].table[idx];
        while(he) {
            if (dictCompareKeys(d, key, he->key))
                return -1;
            he = he->next;
        }
        if (!dictIsRehashing(d)) break;
    }
    return idx;
}

上面这个函数,为获取哈希值和索引值的过程, 首先通过 dictHashKey(d, key) 获取哈希值,这是一个宏函数

#define dictHashKey(d, key) (d)->type->hashFunction(key)

然后根据哈希值求索引值,idx = h & d->ht[table].sizemask,如果此元素在哈希表中已存在,返回-1,不需要再次插入,否则返回一个可以用的位置。搜索哈希表时,需要在两个哈希表中都要搜索。

那么,当 _dictExpandIfNeed (d) 判断出需要扩大哈希表时,是如何扩大哈希表的呢?_dictExpandIfNeed (d)这个函数在判断时:
1. 如果 rehashidx != -1 说明,哈希表在 rehash&

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

猫步旅人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值