redis t_hash哈希对象的源码分析

       哈希对象的编码可以是ziplist或者hashtable。

        ziplist编码的哈希对象使用压缩列表作为底层实现,当新的键值对加入哈希对象时,先将保存健的压缩列表节点添加到压缩列表表尾,然后再将保存值的压缩列表节点添加到表尾。

      hashtable编码的哈希对象使用字典作为底层实现,哈希对象的键值对由字典的键值对来保存。

      当哈希对象同时满足两个条件可以使用ziplist编码:

      1、哈希对象保存的所有键值对的键和值的字符串长度都是小于64字节;

       2、哈希对象的键值对数量小于512;

      不能满足这两个条件,哈希对象会将对象的编码转换成hashtable编码。

一、哈希对象的实现

1、哈希对象的操作

     获取指定key的哈希对象,不存在,为key新建一个哈希对象返回

robj *hashTypeLookupWriteOrCreate(client *c, robj *key) {
    robj *o = lookupKeyWrite(c->db,key);
    if (o == NULL) {
        o = createHashObject();
        dbAdd(c->db,key,o);
    } else {
        if (o->type != OBJ_HASH) {
            addReply(c,shared.wrongtypeerr);
            return NULL;
        }
    }
    return o;
}

   添加键值对到哈希对象中

int hashTypeSet(robj *o, sds field, sds value, int flags) {
    int update = 0;
    if (o->encoding == OBJ_ENCODING_ZIPLIST) {
        unsigned char *zl, *fptr, *vptr;
        zl = o->ptr;
        fptr = ziplistIndex(zl, ZIPLIST_HEAD);
        if (fptr != NULL) {
            fptr = ziplistFind(fptr, (unsigned char*)field, sdslen(field), 1);
            //哈希对象中存在这个field,更新value
            if (fptr != NULL) {
                vptr = ziplistNext(zl, fptr);
                update = 1;
                zl = ziplistDelete(zl, &vptr);//删除存储旧value的压缩列表节点
                zl = ziplistInsert(zl, vptr, (unsigned char*)value,
                        sdslen(value));//在旧value节点的位置添加新value
            }
        }
        if (!update) {
            //哈希对象中不存在这个field,直接将键和值依次添加到压缩列表尾部
            zl = ziplistPush(zl, (unsigned char*)field, sdslen(field),
                    ZIPLIST_TAIL);
            zl = ziplistPush(zl, (unsigned char*)value, sdslen(value),
                    ZIPLIST_TAIL);
        }
        o->ptr = zl;
        //判断哈希对象的键值对数量是否大于512,进行ziplist编码转hashtable编码
        if (hashTypeLength(o) > server.hash_max_ziplist_entries)
            hashTypeConvert(o, OBJ_ENCODING_HT);
    } else if (o->encoding == OBJ_ENCODING_HT) {
        dictEntry *de = dictFind(o->ptr,field);
        if (de) {
            //哈希对象存在field,更新value
            sdsfree(dictGetVal(de));
            if (flags & HASH_SET_TAKE_VALUE) {
                dictGetVal(de) = value;
                value = NULL;
            } else {
                dictGetVal(de) = sdsdup(value);
            }
            update = 1;
        } else {
            sds f,v;//哈希对象中不存在field,将field-value添加到字典中
            if (flags & HASH_SET_TAKE_FIELD) {
                f = field;
                field = NULL;
            } else {
                f = sdsdup(field);
            }
            if (flags & HASH_SET_TAKE_VALUE) {
                v = value;
                value = NULL;
            } else {
                v = sdsdup(value);
            }
            dictAdd(o->ptr,f,v);
        }
    } //释放field和value字符串对象
    if (flags & HASH_SET_TAKE_FIELD && field) sdsfree(field);
    if (flags & HASH_SET_TAKE_VALUE && value) sdsfree(value);
    return update;
}

    删除hash对象指定field的键值对

int hashTypeDelete(robj *o, sds field) {
    int deleted = 0;
    if (o->encoding == OBJ_ENCODING_ZIPLIST) {
        unsigned char *zl, *fptr;
        zl = o->ptr;
        fptr = ziplistIndex(zl, ZIPLIST_HEAD);
        if (fptr != NULL) {//从压缩列表中删除键值对的节点
            fptr = ziplistFind(fptr, (unsigned char*)field, sdslen(field), 1);
            if (fptr != NULL) {
                zl = ziplistDelete(zl,&fptr);
                zl = ziplistDelete(zl,&fptr);
                o->ptr = zl;
                deleted = 1;
            }
        }
    } else if (o->encoding == OBJ_ENCODING_HT) {
        if (dictDelete((dict*)o->ptr, field) == C_OK) {
            deleted = 1;
            //进行收缩字典空间判断,进行字典空间收缩
            if (htNeedsResize(o->ptr)) dictResize(o->ptr);
        }

    }
    return deleted;
}

    其他哈希对象操作的汇总

unsigned long hashTypeLength(const robj *o);//获取哈希对象中元素的个数
int hashTypeGetValue(robj *o, sds field, unsigned char **vstr, unsigned int *vlen, long long *vll);
//获取哈希对象中指定field的value
int hashTypeExists(robj *o, sds field) ;//判断哈希对象是否存在指定field
hashTypeIterator *hashTypeInitIterator(robj *subject);//初始化哈希对象的迭代器
void hashTypeReleaseIterator(hashTypeIterator *hi);//释放哈希对象的迭代器
int hashTypeNext(hashTypeIterator *hi);//将迭代器移到下一个节点 
void hashTypeCurrentObject(hashTypeIterator *hi, int what, unsigned char **vstr, 
    unsigned int *vlen, long long *vll)//获取迭代器指到的键值对 

二、哈希对象的redis命令

1、设置哈希对象的field-value的redis命令

//将哈希表key中的哈希对象的域field的值设置为value
void hsetCommand(client *c) {
    int update;
    robj *o;//查找key对应的对象
    if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;
    hashTypeTryConversion(o,c->argv,2,3);//尝试编码转换
    update = hashTypeSet(o,c->argv[2]->ptr,c->argv[3]->ptr,HASH_SET_COPY);//设置哈希对象的field的val
    addReply(c, update ? shared.czero : shared.cone);//返回操作结果
    signalModifiedKey(c->db,c->argv[1]);//触发被监视键已经被修改的信号
    notifyKeyspaceEvent(NOTIFY_HASH,"hset",c->argv[1],c->db->id);
    server.dirty++;
}
void hsetnxCommand(client *c); /*将哈希表key中的哈希对象域field的值设置为value,当且仅当域field不存在。
相对hsetCommand,在设置哈希对象的field的val的时候,进行field是否存在的判断 */
void hmsetCommand(client *c);/*设置哈希表key中哈希对象多个域field的值,逻辑与hsetCommand,循环调用
hashTypeSet设置传入的多个field-value*/

2、获取哈希对象中的field-value的redis命令

//将哈希对象的指定field的value返回给client
static void addHashFieldToReply(client *c, robj *o, sds field); 
//将哈希表中key对应的哈希对象的指定field的value返回给客户端
void hgetCommand(client *c) {
    robj *o;//获取哈希表中指定key的哈希对象
    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL ||
        checkType(c,o,OBJ_HASH)) return;
    addHashFieldToReply(c, o, c->argv[2]->ptr);
}
void hmgetCommand(client *c);//将哈希表中key对应的哈希对象的指定多个field的value返回给客户端
//返回哈希表中key对应哈希对象中所有field、value或者field-value给客户端
void genericHgetallCommand(client *c, int flags) {
    robj *o;
    hashTypeIterator *hi;
    int multiplier = 0;
    int length, count = 0;
    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptymultibulk)) == NULL
        || checkType(c,o,OBJ_HASH)) return;
    if (flags & OBJ_HASH_KEY) multiplier++;
    if (flags & OBJ_HASH_VALUE) multiplier++;
    length = hashTypeLength(o) * multiplier;
    addReplyMultiBulkLen(c, length);
    hi = hashTypeInitIterator(o);
    //遍历哈希对象
    while (hashTypeNext(hi) != C_ERR) {
        if (flags & OBJ_HASH_KEY) {//将迭代器指向的键值对的key返回给客户端
            addHashIteratorCursorToReply(c, hi, OBJ_HASH_KEY);
            count++;
        }
        if (flags & OBJ_HASH_VALUE) {//将迭代器指向的键值对的value返回给客户端
            addHashIteratorCursorToReply(c, hi, OBJ_HASH_VALUE);
            count++;
        }
    }
}
void hkeysCommand(client *c);//返回哈希表中key对应哈希对象中所有field给客户端
void hvalsCommand(client *c);//返回哈希表中key对应哈希对象中所有value给客户端
void hgetallCommand(client *c);//返回哈希表中key对应哈希对象中所有field-value给客户端

3、其余命令的汇总

void hincrbyCommand(client *c);//当value的值为整形时,添加val
void hincrbyfloatCommand(client *c);//当value的值为浮点数时,添加val
void hdelCommand(client *c);//调用hashTypeDelete删除哈希表中key对应的哈希对象的指定feild
void hlenCommand(client *c);//调用hashTypeLength获取哈希表中key对应的哈希对象的元素个数
void hstrlenCommand(client *c);//获取哈希表中key对应的哈希对象的指定field的value的字符串长度
void hexistsCommand(client *c);//返回给客户端指定key的指定field是否存在
void hscanCommand(client *c) ;//扫描操作

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值