Redis_字典[通俗易懂]

Redis_字典

大家好,又见面了,我是全栈君。

阅读本文之前要了解的两件事情,第一,Redis是一种Key-Value数据库,第二,字典是一种保存键值对的抽象数据结构。所以不难猜出字典在Redis中应用一定很广泛,实际上,Redis数据库的底层实现就是字典,对数据库的增删查改也是构建在对字典的操作上。那么想要深入理解Redis,字典的解密是不可缺少的。接下来,就让我们一层一层解开指点的面纱,看看它的真面目。

首先看看Redis中有哪些地方使用到了字典

一, 数据库键空间

Redis是一个键值对数据库server,server中的每一个数据库都是一个RedisDB结构,当中RedisDb结构的dict字典保存了数据库中的全部键值对。我们将这个字典称为键空间(key space),键空间和用户直接所见的数据库是直接相应的

二。 Expires字典

Redis数据库结构是一个RedisDb结构,有一个属性expires也是字典,这个字典中保存了数据库中全部键的过期时间,我们称这个字典叫做过期字典

以下贴出RedisDb的数据结构。加深了理解。

三。 字典是Hash类型的底层实现之中的一个
这里之所以说是之中的一个。是应为Hash类型的实现能够是多种类型,在不同的场景下能够是不同的类型,但一个哈希键中包括的键值对照较多。有或者是键值对中元素都是比較长的字符串的时候。就会使用字典作为底层实现。否则就是压缩列表作为底层实现。
【注意】键空间中的键和过期字典中的键都指向都一个键对象,所以不会出现不论什么反复对象,也不会浪费内存空间。

然后我们来了解一下在Redis中字典是怎样实现的。

字典的定义在dict.h/dict中给出了,例如以下:

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;

这是一个哈希表。table数组中的每一个元素都是指向一个dictEntry结构的指针,size是哈希表的大小。也就是table数组的大小。sizemask属性总是等于size-1 ,sizemask和哈希值一起决定将一个键应该被放到那个数组上,used表示眼下哈希表有多少个节点,used/size 是一个哈希表的负载因子,这个因子决定了什么时候后对哈希表进行扩展和收缩。

typedef struct dictht {
    dictEntry **table;
    unsigned long size;
    unsigned long sizemask;
    unsigned long used;
} dictht;

以下是一个哈希表节点,每一个dictEntry结构都保持着一个键值对,当中next指针能够将多个哈希值同样的键值对连接在一起,一次来解决键冲突的问题(这里能够引申出哈希函数以及哈希冲突解决方式。Redis中使用的解决方式是链地址法,就是。假设多个值通过哈希函数得到的哈希值是同样的,那么就链接到这个地址后,另一种解决哈希冲突的方案。就是寻地址法。就是当出现哈希冲突的时候,对键值对在进行一个哈希函数。得到一个没有被占用的地址为止,这两种方案各有利弊,链地址法可能会退化成一个链表。寻地址法可能在后期插入时,全是冲突)

typedef struct dictEntry {
    void *key;
    union {
        void *val;
        uint64_t u64;
        int64_t s64;
        double d;
    } v;
    struct dictEntry *next;
} dictEntry;

另一个须要说的地方,就是哈希表的rehash

随着操作的不断运行,一个哈希表中保存的键值对会越来越多或者是越来越少,哈希表中键值对数量过多或者过少都是不好的,过多,就会相当于是多个链表。过少也不好,查找的命中率也会非常低。将哈希表的负载因子(used/size)维持在一个范围之类是最好的。所以,当哈希表的数量过大或者过小的时候。程序会对哈希表进行扩展或者收缩,

扩展好理解,假设size=4 ,可是used=8,相当于每一个键的后面都有个链,这样查找起来是费劲的。这个时候能够通过Rehash来进行完毕,注意dict数据结构中的那个

dictht ht[2],这里是两个dictht,当中ht[1]是空暇的,在进行扩展的时候现将ht[1]扩展成ht[0]的两倍,然后将ht[0]中的键值对一个一个哈希到ht[1]中去。最后将ht[1]设置为ht[0]

这里须要注意的是rehash的时机。通常是负载因子大于5的时候扩展,负载因子小于0.1的时候收缩,另一个问题是字典中有个属性是rehashidx。这个属性标志rehash的状态,假设是0,表示rehash正式開始。然后没rehash一个键值对。就将这个值加一,当ht[0]的值所有被转移到ht[1]的时候。就将这个值设置成-1。表示rehash操作完毕。

事实上还有非常多要说的。比方渐进式rehash,渐进式就说说rehash过程不是一次性完毕的,而是分多次,渐进式完毕的,在rehash过程中,全部的删除,查找。更新都会在两个哈希表中进行。比如,假设查找一个元素,ht[0]中没有,那么就去ht[1]中查找,新加入的一律都是加入到ht[1]中,ht[0]中不再进行不论什么加入操作

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/115372.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • python里的def 方法中->代表什么意思?

    python里的def 方法中->代表什么意思?功能注释函数注释是关于用户定义函数使用的类型的完全可选元数据信息(请参阅PEP3107和 PEP484了解更多信息)。注释__annotations__ 作为字典存储在函数

    2022年7月6日
    25
  • 【ThinkingInC++】61、非成员运算符「建议收藏」

    【ThinkingInC++】61、非成员运算符

    2022年1月17日
    53
  • delphi2007中如何安装ActiveX控件

    delphi2007中如何安装ActiveX控件(1).打开Delphi2007,File-New-PackageDelphiforwin32.(2).Component-ImportComponent,选择ImportActiveXControl.(3).在控件列表,或Add添加选择相应Active控件后,点next.(4).选择安装ActiveX控件所在的面板页,单元,包等.(5)点next,最后一步,选

    2022年5月15日
    39
  • 手机人才需要“山寨”精神[通俗易懂]

    手机人才需要“山寨”精神[通俗易懂] 山寨,颇具江湖味道的词语,隐藏了相当多的非主流文化因素,山寨机的横行使得手机江湖更加暗涛汹涌。由芯片厂商提供集成了软件的芯片,再配上外壳和电池,任何想自立为王的手机厂商用两个月就可以制造出这种移动终端,而正规手机厂商生产没有个一年半载却出不来。山寨机的繁荣给许多国产手机的正规军造成了干扰。一些正规手机企业指责山寨机由于缺乏核心技术和自主创新能力,长期发展下去会拖累中国在手机制造领域的发展水平。不

    2022年9月24日
    5
  • 手把手教你写专利申请书/如何申请专利

    手把手教你写专利申请书/如何申请专利手把手教你写专利申请书·如何申请专利 摘要小前言(一)申请前的准备工作   1、申请前查询   2、其他方面的考虑   3、申请文件准备(二)填写专利申请系列文档   1、实际操作步骤   2、具体操作   3、经验分享、注意事项(三)关于费用(四)其他的话参考资源提示常见问题的问与答 摘要:   如何写好专利申请?由于很多专利申请人都…

    2022年6月11日
    29
  • JavaScript数组splice方法

    JavaScript数组splice方法varnumber=[10,3,4,7];//删除第一位元素,0:下标,1:个数varremoved=number.splice(0,1);console.log(number);//[3,4,7]console.log(removed);//[10]varnumber2=[10,3,4,7];//插入元素5和8,1:下标1开始,0:删除0个元素,…

    2022年9月24日
    3

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号