千家信息网

redis中hash如何实现的

发表于:2025-01-20 作者:千家信息网编辑
千家信息网最后更新 2025年01月20日,这篇文章主要介绍redis中hash如何实现的,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!0.前言redis是KV型的内存数据库, 数据库存储的核心就是Hash表, 我们执行
千家信息网最后更新 2025年01月20日redis中hash如何实现的

这篇文章主要介绍redis中hash如何实现的,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!

0.前言

redis是KV型的内存数据库, 数据库存储的核心就是Hash表, 我们执行select命令选择一个存储的db之后, 所有的操作都是以hash表为基础的, 下面会分析下redis的hash数据结构和实现.

1.hash数据结构

/*Hash表一个节点包含Key,Value数据对 */typedef struct dictEntry {    void *key;    union {        void *val;        uint64_t u64;        int64_t s64;        double d;    } v;    struct dictEntry *next; /* 指向下一个节点, 链接表的方式解决Hash冲突 */} dictEntry;/* 存储不同数据类型对应不同操作的回调函数 */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;typedef struct dictht {    dictEntry **table; /* dictEntry*数组,Hash表 */    unsigned long size; /* Hash表总大小 */    unsigned long sizemask; /* 计算在table中索引的掩码, 值是size-1 */    unsigned long used; /* Hash表已使用的大小 */} dictht;typedef struct dict {    dictType *type;    void *privdata;    dictht ht[2]; /* 两个hash表,rehash时使用*/    long rehashidx; /* rehash的索引, -1表示没有进行rehash */    int iterators; /*  */} dict;

2.hash数据结构图

3.渐进式hash说明

dict中ht[2]中有两个hash表, 我们第一次存储数据的数据时, ht[0]会创建一个最小为4的hash表, 一旦ht[0]中的size和used相等, 则dict中会在ht[1]创建一个size*2大小的hash表, 此时并不会直接将ht[0]中的数据copy进ht[0]中, 执行的是渐进式rehash, 即在以后的操作(find, set, get等)中慢慢的copy进去, 以后新添加的元素会添加进ht[0], 因此在ht[1]被占满的时候定能确保ht[0]中所有的数据全部copy到ht[1]中.

4.创建hash表

创建hash表过程非常简单,直接调用dictCreate函数, 分配一块内存,初始化中间变量即可.

dict *dictCreate(dictType *type, void *privDataPtr){     /*分配内存*/    dict *d = zmalloc(sizeof(*d));     /*初始化操作*/    _dictInit(d,type,privDataPtr);    return d;}

5.添加元素

hash表中添加元素,首先判断空间是否足够, 然后计算key对应的hash值, 然后将需要添加的key和value放入表中.

int dictAdd(dict *d, void *key, void *val){     /*添加入hash表中, 返回新添加元素的实体结构体*/    dictEntry *entry = dictAddRaw(d,key);    if (!entry) return DICT_ERR;     /*元素val值放入元素实体结构中*/    dictSetVal(d, entry, val);    return DICT_OK;}/**添加元素实体函数*/dictEntry *dictAddRaw(dict *d, void *key){    int index;    dictEntry *entry;    dictht *ht;    if (dictIsRehashing(d)) _dictRehashStep(d);    /*根据key值计算新元素在hash表中的索引, 返回-1则表示元素已存在, 直接返回NULL*/    if ((index = _dictKeyIndex(d, key)) == -1)        return NULL;    /*如果在进行rehash过程,则新元素添加到ht[1]中, 否则添加到ht[0]中 */    ht = dictIsRehashing(d) ? &d->ht[1] : &d->ht[0];    entry = zmalloc(sizeof(*entry));    entry->next = ht->table[index];    ht->table[index] = entry;    ht->used++;    /*设置元素key*/    dictSetKey(d, entry, key);    return entry;}/**计算索引的函数*/static int _dictKeyIndex(dict *d, const void *key){    unsigned int h, idx, table;    dictEntry *he;    /* 判断hash表是否空间足够, 不足则需要扩展 */    if (_dictExpandIfNeeded(d) == DICT_ERR)        return -1;             /* 计算key对应的hash值 */    h = dictHashKey(d, key);    for (table = 0; table <= 1; table++) {          /*计算索引*/        idx = h & d->ht[table].sizemask;        /*遍历冲突列表, 判断需要查找的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;}/**判断hash表是否需要扩展空间*/static int _dictExpandIfNeeded(dict *d){    /*redis的rehash采用的渐进式hash, rehash时分配了原来两倍的内存空间, 在rehash阶段空间必定够用*/    if (dictIsRehashing(d)) return DICT_OK;    /* hash表是空的需要初始化空间, 默认是4*/    if (d->ht[0].size == 0) return dictExpand(d, DICT_HT_INITIAL_SIZE);    /* 已使用空间满足不了设置的条件*/    if (d->ht[0].used >= d->ht[0].size &&        (dict_can_resize ||         d->ht[0].used/d->ht[0].size > dict_force_resize_ratio))    {          /*扩展空间, 使用空间的两倍*/        return dictExpand(d, d->ht[0].used*2);    }    return DICT_OK;}/**扩展空间或者初始化hash表空间*/int dictExpand(dict *d, unsigned long size){    dictht n;     /* 对需要分配大小圆整为2的倍数 */    unsigned long realsize = _dictNextPower(size);    /* 如果空间足够则表明调用错误 */    if (dictIsRehashing(d) || d->ht[0].used > size)        return DICT_ERR;    n.size = realsize;    n.sizemask = realsize-1;    n.table = zcalloc(realsize*sizeof(dictEntry*));    n.used = 0;         /*hash表为空初始化hash表*/    if (d->ht[0].table == NULL) {        d->ht[0] = n;        return DICT_OK;    }    /*新分配的空间放入ht[1], 后面一步一步进行rehash*/    d->ht[1] = n;    d->rehashidx = 0;    return DICT_OK;}

6.查找元素

查找元素过程,首先计算hash值, 然后计算在ht[0]和ht[1]中索引位置, 进行查找.

dictEntry *dictFind(dict *d, const void *key){    dictEntry *he;    unsigned int h, idx, table;    if (d->ht[0].size == 0) return NULL;         /*如果正在进行rehash, 执行一次rehash*/    if (dictIsRehashing(d)) _dictRehashStep(d);        h = dictHashKey(d, key);         /*由于可能正在rehash, 因此要从ht[0]和ht[1]中分别进行查找, 找不到返回NULL*/    for (table = 0; table <= 1; table++) {        idx = h & d->ht[table].sizemask;        he = d->ht[table].table[idx];          /*遍历冲突列表查找元素*/        while(he) {            if (dictCompareKeys(d, key, he->key))                return he;            he = he->next;        }        if (!dictIsRehashing(d)) return NULL;    }    return NULL;}

7.删除元素

删除元素首先查找元素, 然后将元素从hash表中移除即可, 调用dictDelete删除元素, 会同时删除元素所占空间

int dictDelete(dict *ht, const void *key) {    return dictGenericDelete(ht,key,0);}static int dictGenericDelete(dict *d, const void *key, int nofree){    unsigned int h, idx;    dictEntry *he, *prevHe;    int table;    if (d->ht[0].size == 0) return DICT_ERR;        if (dictIsRehashing(d)) _dictRehashStep(d);    h = dictHashKey(d, key);    for (table = 0; table <= 1; table++) {        idx = h & d->ht[table].sizemask;        he = d->ht[table].table[idx];        prevHe = NULL;          /*查找元素到元素,进行删除操作, 并释放占用的内存*/        while(he) {            if (dictCompareKeys(d, key, he->key)) {                /* Unlink the element from the list */                if (prevHe)                    prevHe->next = he->next;                else                    d->ht[table].table[idx] = he->next;                if (!nofree) {                    dictFreeKey(d, he);                    dictFreeVal(d, he);                }                zfree(he);                d->ht[table].used--;                return DICT_OK;            }            prevHe = he;            he = he->next;        }        if (!dictIsRehashing(d)) break;    }    return DICT_ERR; /* not found */}

hash命令

hash命令操作都比较简单,需要注意的是当我们创建hash表示默认存储结构,并不是dict,而是ziplist结构,可以参考redis之Ziplist数据结构,hash_max_ziplist_entries和hash_max_ziplist_value值作为阀值,hash_max_ziplist_entries表示一旦ziplist中元素数量超过该值,则需要转换为dict结构;hash_max_ziplist_value表示一旦ziplist中数据长度大于该值,则需要转换为dict结构。

以上是redis中hash如何实现的的所有内容,感谢各位的阅读!希望分享的内容对大家有帮助,更多相关知识,欢迎关注行业资讯频道!

元素 空间 数据 结构 内存 索引 存储 函数 大小 冲突 分配 命令 实体 数据结构 渐进式 过程 不同 两个 内容 数据库 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 软件开发销售主管的工作总结 消化内科重点目标单位数据库 ai网络安全教学 万方数据库删除论文怎么删除 忍三为什么要等服务器开启 新华三网络安全产品解决方案 把数据库索引删了 怎样快速统计后十名的数据库 工控网络技术一般几年级学 是德科技工业互联网 数据库查询表格最后一条数据 诛仙流波区服务器发展史 如何打开并解释音频数据库 标题网络安全研究的内容有哪些 茨菇塘街道软件开发培训 人民人寿险软件开发offer 三精广告数据库15秒 申请软件开发项目奖金 嵌入式软件开发操作系统 敏捷式软件开发pdf 琦玩网络技术 琼海云服务器一键搭建网站环境 软件开发业务很重吗 时代网络技术分类优势 杭州职高计算机网络技术 网络安全技术接入交换机部署 方舟生存进化反作弊服务器 查看数据库文件的命令 怎么修改服务器管理员权限 saperp数据库使用方法
0