t_hash.c

作者: 生命就是个Bug | 来源:发表于2019-04-16 18:23 被阅读0次

    Redis的t_hash.c是对hash字典数据结构的实现。
    主要是基于ziplistdictsds实现。

    当元素较少的时候,使用ziplist来实现字典数据结构。
    当元素较多的时候,使用dict来实现字典数据结构。

    每次添加元素的时候,都会去检查是否需要转换类型。

    在redis.conf中存在如下配置:

    # Hashes are encoded using a memory efficient data structure when they have a
    # small number of entries, and the biggest entry does not exceed a given
    # threshold. These thresholds can be configured using the following directives.
    hash-max-ziplist-entries 512
    hash-max-ziplist-value 64
    

    t_hash.c存在如下转换ziplist和dict的代码:

    /* Check the length of a number of objects to see if we need to convert a
     * ziplist to a real hash. Note that we only check string encoded objects
     * as their string length can be queried in constant time. */
    //当元素较少的时候,用ziplist存储,数量较多的时候转为Hash存储。
    void hashTypeTryConversion(robj *o, robj **argv, int start, int end) {
        int i;
    
        if (o->encoding != OBJ_ENCODING_ZIPLIST) return;
    
        //达到hash_max_ziplist_value的时候,会转为dict
        for (i = start; i <= end; i++) {
            if (sdsEncodedObject(argv[i]) &&
                sdslen(argv[i]->ptr) > server.hash_max_ziplist_value)
            {
                hashTypeConvert(o, OBJ_ENCODING_HT);
                break;
            }
        }
    }
    

    1. 新增元素

    //hset命令
    void hsetCommand(client *c) {
        int i, created = 0;
        robj *o;
    
        if ((c->argc % 2) == 1) {
            addReplyError(c,"wrong number of arguments for HMSET");
            return;
        }
    
        //根据key查找
        if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;
        //尝试转换类型ziplist -> dict
        hashTypeTryConversion(o,c->argv,2,c->argc-1);
    
        for (i = 2; i < c->argc; i += 2)
            created += !hashTypeSet(o,c->argv[i]->ptr,c->argv[i+1]->ptr,HASH_SET_COPY);
    
        /* HMSET (deprecated) and HSET return value is different. */
        char *cmdname = c->argv[0]->ptr;
        if (cmdname[1] == 's' || cmdname[1] == 'S') {
            /* HSET */
            addReplyLongLong(c, created);
        } else {
            /* HMSET */
            addReply(c, shared.ok);
        }
        //事务 watch key
        signalModifiedKey(c->db,c->argv[1]);
        notifyKeyspaceEvent(NOTIFY_HASH,"hset",c->argv[1],c->db->id);
        server.dirty++;
    }
    
    //hsetnx命令
    void hsetnxCommand(client *c) {
        robj *o;
        //根据key查找
        if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;
        //类型转换
        hashTypeTryConversion(o,c->argv,2,3);
    
        //判断field是否存在
        if (hashTypeExists(o, c->argv[2]->ptr)) {
            addReply(c, shared.czero);
        } else {
            //添加
            hashTypeSet(o,c->argv[2]->ptr,c->argv[3]->ptr,HASH_SET_COPY);
            addReply(c, shared.cone);
            signalModifiedKey(c->db,c->argv[1]);
            notifyKeyspaceEvent(NOTIFY_HASH,"hset",c->argv[1],c->db->id);
            server.dirty++;
        }
    }
    
    
    static void addHashFieldToReply(client *c, robj *o, sds field) {
        int ret;
    
        //如果为空,返回"$-1\r\n"
        if (o == NULL) {
            addReply(c, shared.nullbulk);
            return;
        }
    
        //ziplist
        if (o->encoding == OBJ_ENCODING_ZIPLIST) {
            unsigned char *vstr = NULL;
            unsigned int vlen = UINT_MAX;
            long long vll = LLONG_MAX;
    
            //根据field获取
            ret = hashTypeGetFromZiplist(o, field, &vstr, &vlen, &vll);
            if (ret < 0) {
                //如果为空,返回"$-1\r\n"
                addReply(c, shared.nullbulk);
            } else {
                if (vstr) {
                    addReplyBulkCBuffer(c, vstr, vlen);
                } else {
                    addReplyBulkLongLong(c, vll);
                }
            }
    
        }
        //dict
        else if (o->encoding == OBJ_ENCODING_HT) {
            sds value = hashTypeGetFromHashTable(o, field);
            if (value == NULL)
                addReply(c, shared.nullbulk);
            else
                addReplyBulkCBuffer(c, value, sdslen(value));
        } else {
            serverPanic("Unknown hash encoding");
        }
    }
    

    2. 删除元素

    //hdel命令
    void hdelCommand(client *c) {
        robj *o;
        int j, deleted = 0, keyremoved = 0;
    
        //根据key查找并校验类型
        if ((o = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL ||
            checkType(c,o,OBJ_HASH)) return;
    
        //删除
        for (j = 2; j < c->argc; j++) {
            if (hashTypeDelete(o,c->argv[j]->ptr)) {
                deleted++;
                if (hashTypeLength(o) == 0) {
                    dbDelete(c->db,c->argv[1]);
                    keyremoved = 1;
                    break;
                }
            }
        }
    
        if (deleted) {
            signalModifiedKey(c->db,c->argv[1]);
            notifyKeyspaceEvent(NOTIFY_HASH,"hdel",c->argv[1],c->db->id);
            if (keyremoved)
                notifyKeyspaceEvent(NOTIFY_GENERIC,"del",c->argv[1],
                                    c->db->id);
            server.dirty += deleted;
        }
    
        //响应client
        addReplyLongLong(c,deleted);
    }
    

    3. 查找元素

    //hget命令
    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);
    }
    
    //hmget命令
    void hmgetCommand(client *c) {
        robj *o;
        int i;
    
        /* Don't abort when the key cannot be found. Non-existing keys are empty
         * hashes, where HMGET should respond with a series of null bulks. */
        //根据key查找
        o = lookupKeyRead(c->db, c->argv[1]);
        if (o != NULL && o->type != OBJ_HASH) {
            addReply(c, shared.wrongtypeerr);
            return;
        }
    
        addReplyMultiBulkLen(c, c->argc-2);
        //返回
        for (i = 2; i < c->argc; i++) {
            addHashFieldToReply(c, o, c->argv[i]->ptr);
        }
    }
    

    4. 增加value值

    //hincrby命令
    void hincrbyCommand(client *c) {
        long long value, incr, oldvalue;
        robj *o;
        sds new;
        unsigned char *vstr;
        unsigned int vlen;
    
        //增加的值,类型转换,值处理
        if (getLongLongFromObjectOrReply(c,c->argv[3],&incr,NULL) != C_OK) return;
        //根据key查找
        if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;
        //获取当前value
        if (hashTypeGetValue(o,c->argv[2]->ptr,&vstr,&vlen,&value) == C_OK) {
            if (vstr) {
                if (string2ll((char*)vstr,vlen,&value) == 0) {
                    addReplyError(c,"hash value is not an integer");
                    return;
                }
            } /* Else hashTypeGetValue() already stored it into &value */
        } else {
            value = 0;
        }
    
        oldvalue = value;
        //判断是否越域
        if ((incr < 0 && oldvalue < 0 && incr < (LLONG_MIN-oldvalue)) ||
            (incr > 0 && oldvalue > 0 && incr > (LLONG_MAX-oldvalue))) {
            addReplyError(c,"increment or decrement would overflow");
            return;
        }
        //赋值
        value += incr;
        new = sdsfromlonglong(value);
        //修改value,HASH_SET_TAKE_VALUE
        hashTypeSet(o,c->argv[2]->ptr,new,HASH_SET_TAKE_VALUE);
        //响应client
        addReplyLongLong(c,value);
        signalModifiedKey(c->db,c->argv[1]);
        notifyKeyspaceEvent(NOTIFY_HASH,"hincrby",c->argv[1],c->db->id);
        server.dirty++;
    }
    
    //hincrbyfloat命令
    void hincrbyfloatCommand(client *c) {
        long double value, incr;
        long long ll;
        robj *o;
        sds new;
        unsigned char *vstr;
        unsigned int vlen;
    
        //增加的值,类型转换,值处理
        if (getLongDoubleFromObjectOrReply(c,c->argv[3],&incr,NULL) != C_OK) return;
        //根据key查找
        if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;
        //获取原来的值
        if (hashTypeGetValue(o,c->argv[2]->ptr,&vstr,&vlen,&ll) == C_OK) {
            if (vstr) {
                if (string2ld((char*)vstr,vlen,&value) == 0) {
                    addReplyError(c,"hash value is not a float");
                    return;
                }
            } else {
                value = (long double)ll;
            }
        } else {
            value = 0;
        }
    
        //增加
        value += incr;
        if (isnan(value) || isinf(value)) {
            addReplyError(c,"increment would produce NaN or Infinity");
            return;
        }
    
        char buf[MAX_LONG_DOUBLE_CHARS];
        int len = ld2string(buf,sizeof(buf),value,1);
        new = sdsnewlen(buf,len);
        //修改value
        hashTypeSet(o,c->argv[2]->ptr,new,HASH_SET_TAKE_VALUE);
        addReplyBulkCBuffer(c,buf,len);
        signalModifiedKey(c->db,c->argv[1]);
        notifyKeyspaceEvent(NOTIFY_HASH,"hincrbyfloat",c->argv[1],c->db->id);
        server.dirty++;
    
        /* Always replicate HINCRBYFLOAT as an HSET command with the final value
         * in order to make sure that differences in float pricision or formatting
         * will not create differences in replicas or after an AOF restart. */
    
        //新值引用次数增加,老值引用次数减少
        robj *aux, *newobj;
        aux = createStringObject("HSET",4);
        newobj = createRawStringObject(buf,len);
        rewriteClientCommandArgument(c,0,aux);
        decrRefCount(aux);
        rewriteClientCommandArgument(c,3,newobj);
        decrRefCount(newobj);
    }
    

    5. 长度统计

    //hlen命令
    void hlenCommand(client *c) {
        robj *o;
    
        if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
            checkType(c,o,OBJ_HASH)) return;
    
        addReplyLongLong(c,hashTypeLength(o));
    }
    
    //hstrlen命令
    void hstrlenCommand(client *c) {
        robj *o;
    
        if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
            checkType(c,o,OBJ_HASH)) return;
        addReplyLongLong(c,hashTypeGetValueLength(o,c->argv[2]->ptr));
    }
    

    6. 获取所有

    //hkeys命令 OBJ_HASH_KEY
    void hkeysCommand(client *c) {
        genericHgetallCommand(c,OBJ_HASH_KEY);
    }
    
    //hvals命令 OBJ_HASH_VALUE
    void hvalsCommand(client *c) {
        genericHgetallCommand(c,OBJ_HASH_VALUE);
    }
    
    //hgetall命令 OBJ_HASH_KEY|OBJ_HASH_VALUE
    void hgetallCommand(client *c) {
        genericHgetallCommand(c,OBJ_HASH_KEY|OBJ_HASH_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);
        //遍历dict
        while (hashTypeNext(hi) != C_ERR) {
            if (flags & OBJ_HASH_KEY) {
                addHashIteratorCursorToReply(c, hi, OBJ_HASH_KEY);
                count++;
            }
            if (flags & OBJ_HASH_VALUE) {
                addHashIteratorCursorToReply(c, hi, OBJ_HASH_VALUE);
                count++;
            }
        }
    
        hashTypeReleaseIterator(hi);
        serverAssert(count == length);
    }
    

    相关文章

      网友评论

          本文标题:t_hash.c

          本文链接:https://www.haomeiwen.com/subject/dyzvwqtx.html