Redis——字典

Redis——字典

​ 字典,又称为符号表、关联数组或映射(map),是一种用于保存键值对的抽象数据结构

​ 在字典中,一个键key映射一个值value ,通过key来操作value ,因此字典中的key必须是唯一的

​ 在Redis中,字典被用于Redis数据库的底层实现,也就是说我们存储的五大Redis数据类型String,List,Set,Zset,Hash 都被存储在字典里,并且数据类型Hash的底层实现之一也是字段

字典是实现

​ Redis的字典使用哈希表作为底层实现,一个哈希表里面有多个哈希表节点,每一个哈希节点保存了字典中的一个键值对(就是套娃)

  • Redis的哈希表使用dict.h/dictht 结构体定义
typedef struct dictdt {
  //哈希表数组,类型为dictEntry
  dictEntry **table;
  //哈希表大小,即table数组的长度
  unsigned long size;
  //哈希表大小掩码,用于计算索引值
  //sizemask的大小总是等于size-1,在Java中的HashMap结构中计算索引值使用table.length和hash(key)
  //而Redis则是table.length-1和hash(key)
  unsigned long sizemask;
  //哈希表节点数量
  unsigned long used;
}dictht;
  • 哈希表节点使用dictEntry结构表示,每个dictEntry结构都保存着一个键值对
typedef struct dictEntry {
  //键,可以是任意类型(指向其他类型的一个指针)
  void *key;
  //值
  //可以是任意类型,uint64_t整数、int64_t整数
  union{
    void *val;
    uint64_t u64;
    int64_t s64;
  }v;
  //保存该下一个节点的指针,解决哈希冲突
  struct dictEntry *next;
}dictEntry;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rQFL6k9N-1632358186221)(images/dictht.png)]

  • Redis中的字典由dict.h/dict 结构表示
typedef struct dict {
  //类型特定函数
  //因为Redis中的数据库使用字典dict来存储数据,故字典中能存储String,List,Set等不同的数据类型
  //而不同的数据类型的操作行为不同,那么久需要为特定的数据类型指定特定的操作函数
  //每个dictType结构中保存了一簇用于操作特定类型键值对的函数
  dictType *type;
  //私有数据
  //可选的传入特定类型操作函数的参数信息
  void *privdata;
  //哈希表
  //哈希表定义数组长度为2的目的是在进行rehash时将哈希表中的键值对rehash到另一个数组中,起辅助作用
  dictht ht[2];
  //记录rehash时的索引下标,不进行rehash时为-1
  //Redis的rehash采用渐进式rehash方式,通过trehashidx的值将该索引下的键值对rehash到ht[1]中
  int trehashidx;
}dict;
typedef struct dictType {
  unsigned int (*hashFunction)(const void *key);
  void *(*keyDup)(void *privdata, const void *key);
  ...
}dictType;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TgXRt1w0-1632358186223)(images/dict.png)]

哈希冲突

​ Redis中解决哈希冲突采用的是链地址法,并且是头插入法,即新添加的节点插入到已有链表的头部,这样就可以将插入的时间复杂度控制在O(1)

rehash

​ 当哈希表中的节点越来越多时,必将会造成更多的哈希冲突,从而增加链表的长度时查询的效率降低,因此在哈希节点达到某个条件时需要进行哈希表的扩充,或者当哈希节点减少时进行缩小,避免过多的节点都在一个索引下标中形成一个长链表,而其他索引下标没有节点导致的浪费,那么则需要进行rehash的过程

  • 哈希表rehash的过程

    1. 为字典的ht[1]哈希表分配空间用于存储rehash后的键值对,而分配的空间大小取决于要执行的操作以及ht[0]当前包含的键值对数量(ht[0].used的值),并且保证哈希表的数组大小为2的n次幂
    • 扩展操作:ht[1]的空间大小为:ht[1].length >= ht[0].used * 22^n
    • 缩小操作:ht[1]的空间大小为:ht[1].length >= ht[0].used的2^n`
    1. 将保存在ht[0]中的所有键值对rehash到ht[1]中
    2. 当ht[0]中的所有键值对都rehash到ht[1]后,释放ht[0]并将ht[1]设置为ht[0],再创建出一个新的空白的ht[1],为下一次rehash准备,也就是说所有的键值对操作一般都在ht[0]中

渐进式rehash

​ 在将ht[0]中的键值对rehash到ht[1]中时,并不是一次性完成的,因为当ht[0]中的键值对的数量足够大时进行一次性rehash时十分耗时,而是采用渐进式rehash的方式逐渐将ht[0]中的键值对rehash的ht[1]中

  1. 为ht[1]分配内存空间,让字典同时持有两个数组ht[0]、ht[1]
  2. 在字典中维护一个索引计数器rehashidx,将他的值设置为0,并在每次rehash结束后自增1,直到ht[0]中没有键值对
  3. 在rehash进行期间,每次对字典执行添加、删除、查找等操作时,程序除了执行以上操作外,顺便将ht[0]中索引为rehashidx下的所有键值对rehash到ht[1]中,之后rehashidx自增1
  4. 随着字典操作次数的增加,rehashidx的值不断增大,渐渐将ht[0]中的键值对都rehash到ht[1]中,并且当执行的操作为添加时是直接在ht[1]数组中执行的,而其他操作为先到ht[0]中执行,如果ht[0]到没有要执行的键值对,则到ht[1]中查询

字典API

函数作用
dictCreate创建一个新的字典
dictAdd将给定的键值对添加到字典里面
dictDelete从字典中删除指定的键值对
dictReplace替换键值对
dictFetchValue返回给定将的值
dictGetRandomKey随机返回字典中的键值对
dictRelease释放给定字典,即删除字典中的所有键值对
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值