侯体宗的博客
  • 首页
  • Hyperf版
  • beego仿版
  • 人生(杂谈)
  • 技术
  • 关于我
  • 更多分类
    • 文件下载
    • 文字修仙
    • 中国象棋ai
    • 群聊
    • 九宫格抽奖
    • 拼图
    • 消消乐
    • 相册

redis的hash怎么实现的

Redis  /  管理员 发布于 7年前   144

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数据结构图

339657-20151020231754692-188838154.png

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相关技术文章,请访问Redis教程栏目进行学习!

以上就是redis的hash怎么实现的的详细内容,更多请关注其它相关文章!


  • 上一条:
    redis为什么16384个槽
    下一条:
    redis集群怎么搭建
  • 昵称:

    邮箱:

    0条评论 (评论内容有缓存机制,请悉知!)
    最新最热
    • 分类目录
    • 人生(杂谈)
    • 技术
    • linux
    • Java
    • php
    • 框架(架构)
    • 前端
    • ThinkPHP
    • 数据库
    • 微信(小程序)
    • Laravel
    • Redis
    • Docker
    • Go
    • swoole
    • Windows
    • Python
    • 苹果(mac/ios)
    • 相关文章
    • 在Redis中能实现的功能、常见应用介绍(0个评论)
    • 2024年Redis面试题之一(0个评论)
    • 在redis缓存常见出错及解决方案(0个评论)
    • 在redis中三种特殊数据类型:地理位置、基数(cardinality)估计、位图(Bitmap)使用场景介绍浅析(2个评论)
    • Redis 删除 key用 del 和 unlink 有啥区别?(1个评论)
    • 近期文章
    • 在go语言中使用api.geonames.org接口实现根据国际邮政编码获取地址信息功能(1个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf分页文件功能(0个评论)
    • gmail发邮件报错:534 5.7.9 Application-specific password required...解决方案(0个评论)
    • 欧盟关于强迫劳动的规定的官方举报渠道及官方举报网站(0个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf文件功能(0个评论)
    • Laravel从Accel获得5700万美元A轮融资(0个评论)
    • 在go + gin中gorm实现指定搜索/区间搜索分页列表功能接口实例(0个评论)
    • 在go语言中实现IP/CIDR的ip和netmask互转及IP段形式互转及ip是否存在IP/CIDR(0个评论)
    • PHP 8.4 Alpha 1现已发布!(0个评论)
    • Laravel 11.15版本发布 - Eloquent Builder中添加的泛型(0个评论)
    • 近期评论
    • 122 在

      学历:一种延缓就业设计,生活需求下的权衡之选中评论 工作几年后,报名考研了,到现在还没认真学习备考,迷茫中。作为一名北漂互联网打工人..
    • 123 在

      Clash for Windows作者删库跑路了,github已404中评论 按理说只要你在国内,所有的流量进出都在监控范围内,不管你怎么隐藏也没用,想搞你分..
    • 原梓番博客 在

      在Laravel框架中使用模型Model分表最简单的方法中评论 好久好久都没看友情链接申请了,今天刚看,已经添加。..
    • 博主 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 @1111老铁这个不行了,可以看看近期评论的其他文章..
    • 1111 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 网站不能打开,博主百忙中能否发个APP下载链接,佛跳墙或极光..
    • 2017-12
    • 2020-03
    • 2020-05
    • 2021-04
    • 2022-03
    • 2022-05
    • 2022-08
    • 2023-02
    • 2023-04
    • 2023-07
    • 2024-01
    • 2024-02
    Top

    Copyright·© 2019 侯体宗版权所有· 粤ICP备20027696号 PHP交流群

    侯体宗的博客