Redis数据结构——字典

字典适用于保存键值对的抽象数据结构,一个键可以和一个值进行关联,在字典中,键是唯一的。Redis的数据库是用字典作为底层实现的,字典还是哈希键的底层实现之一。

1.字典的数据结构

Redis的字典使用哈希表作为底层实现,哈希表由一个或多个哈希节点构成,每个哈希节点保存了字典的一个键值对。

哈希表:

typedef struct dictht {
    dictEntry **table;  // 哈希表数组
    unsigned long size;  // 哈希表大小
    unsigned long sizemask;  // 掩码,用于计算索引值
    unsigned long used;  // 哈希表已有节点数量
} dictht;

 哈希节点:

typedef struct dictEntry {
    void *key;  // 键
    union {  // 值
        void *val;
        uint64_t u64;
        int64_t s64;
        double d;
    } v;
    struct dictEntry *next;  // 下个哈希表节点
} dictEntry;

字典:持有大小为2的哈希表数组,一般情况下只用到ht[0],当执行rehash时才会用到ht[1]

typedef struct dict {
    dictType *type;  // 类型特定函数
    void *privdata;  // 私有数据
    dictht ht[2];  // 哈希表
    long rehashidx;  // rehash索引,当rehash不在进行时,值为-1
    int16_t pauserehash;  // >0时表示rehash暂停
} dict;

2. 字典使用

(1)哈希算法:当要将一个新的键值放入字典时,需要根据键值对的键计算出哈希值和索引值。Redis通过MurmurHash2 算法来计算键的哈希值,然后将得到的哈希值与哈希表的sizemask进行与运算得到键的索引值,根据索引值决定包含键值对的哈希节点在哈希表的存放位置。

(2)键冲突:当两个或以上数量的键分配到哈希表的同一索引时,即产生了键冲突。Redis是通过链地址法解决键冲突的,索引值相同的哈希节点通过next指针连接构成一个单向链表。考虑到插入速度,一般将新插入的节点添加到链表的表头位置,复杂度为O(1)。

(3)rehash:为了让哈希表的负载因子维持在一个较为合理的范围内,当哈希表保存的键值对数量过多或过少时,都会对哈希表进行相应的扩展或收缩,该工作通过rehash完成。

  • 首先为ht[1]分配空间,若为扩展操作,ht[1]的大小则为第一个大于ht[0].used*2的2^{n};若为收缩操作,ht[1]大小则为第一个大于ht[0].used的2^{n}
  • 将ht[0]的所有键值对重新计算哈希值和索引值,并放到ht[1]中
  • 所有键值对迁移完成后,释放ht[0],将ht[1]设置为ht[0],并在ht[1]创建一个空的哈希表,为下一次rehash做准备。

(4)渐进式rehash:为了避免对Redis的性能造成影响,rehash是分多次、渐进式地完成的。字典中维护了一个rehashidx变量,其值为0时表示rehash工作正式开始,当rehash工作完成后会将变量的 值设置为-1。每次对字典执行增删改查操作时,执行完相应操作后会顺带将ht[0]哈希表在rehashidx索引上的所有键值对迁移至ht[1]哈希表,并将rehashidx的值+1。渐进式rehash避免了集中式rehash带来的庞大计算量。 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值