Redis源码--HSET和ZREVRANK

HSET命令

void hsetCommand(redisClient *c) {
    int update;
    robj *o;

    if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;
    //检查是否需要类型转换
    hashTypeTryConversion(o,c->argv,2,3);
    //将参数进行编码
    hashTypeTryObjectEncoding(o,&c->argv[2], &c->argv[3]);
    //更新值:Return 0 on insert and 1 on update.
    update = hashTypeSet(o,c->argv[2],c->argv[3]);

    addReply(c, update ? shared.czero : shared.cone);

    /* "Touch" a key, so that if this key is being WATCHed by
    some client the next EXEC will fail. */
    signalModifiedKey(c->db,c->argv[1]);
    //The API provided to the rest of the Redis core is a simple function
    notifyKeyspaceEvent(REDIS_NOTIFY_HASH,"hset",c->argv[1],c->db->id);
    // dirty:Changes to DB from the last save 
    server.dirty++;
}

1、类型转换处理ziplist-->hashtable

void hashTypeTryConversion(robj *o, robj **argv, int start, int end) {
    int i;

    if (o->encoding != REDIS_ENCODING_ZIPLIST) return;

    for (i = start; i <= end; i++) { //对ziplist中的每个节点都进行转换
        if (sdsEncodedObject(argv[i]) &&
            sdslen(argv[i]->ptr) > server.hash_max_ziplist_value)
        {
            hashTypeConvert(o, REDIS_ENCODING_HT);
            break;
        }
    }
}

hashTypeConvert:

void hashTypeConvertZiplist(robj *o, int enc) { //被hashTypeConvert函数又封装了一层
    redisAssert(o->encoding == REDIS_ENCODING_ZIPLIST);

    if (enc == REDIS_ENCODING_ZIPLIST) {
        /* Nothing to do... */

    } else if (enc == REDIS_ENCODING_HT) {
        hashTypeIterator *hi;
        dict *dict;
        int ret;

        hi = hashTypeInitIterator(o);
        dict = dictCreate(&hashDictType, NULL);//新创建字典

        while (hashTypeNext(hi) != REDIS_ERR) {
            robj *field, *value;

            field = hashTypeCurrentObject(hi, REDIS_HASH_KEY);//键对象
            field = tryObjectEncoding(field);
            value = hashTypeCurrentObject(hi, REDIS_HASH_VALUE);//值对象
            value = tryObjectEncoding(value);
            ret = dictAdd(dict, field, value);//字典的API:新键值对添加到字典
            if (ret != DICT_OK) {
                redisLogHexDump(REDIS_WARNING,"ziplist with dup elements dump",
                    o->ptr,ziplistBlobLen(o->ptr));
                redisAssert(ret == DICT_OK);
            }
        }

        //释放内存
        hashTypeReleaseIterator(hi);
        zfree(o->ptr);

        o->encoding = REDIS_ENCODING_HT;
        o->ptr = dict;

    } else {
        redisPanic("Unknown hash encoding");
    }
}

hashTypeSet:对已有的更新,没有就插入。 返回值:insert:0,update:1

int hashTypeSet(robj *o, robj *field, robj *value) {
    int update = 0;

    if (o->encoding == REDIS_ENCODING_ZIPLIST) {//对ziplist处理
        unsigned char *zl, *fptr, *vptr;
        field = getDecodedObject(field); //该操作会让refcount+1
        value = getDecodedObject(value);
        zl = o->ptr;
        
        //判断是update还是insert
        fptr = ziplistIndex(zl, ZIPLIST_HEAD);
        if (fptr != NULL) {
            //检查是否有这个值
            fptr = ziplistFind(fptr, field->ptr, sdslen(field->ptr), 1);
            if (fptr != NULL) {//有的话更新
                /* Grab pointer to the value (fptr points to the field) */
                vptr = ziplistNext(zl, fptr); //value在key后面
                redisAssert(vptr != NULL);
                update = 1;

                //删除value
                zl = ziplistDelete(zl, &vptr);
                //新增value
                zl = ziplistInsert(zl, vptr, value->ptr, sdslen(value->ptr));
            }
        }
        if (!update) {//原ziplist中没有的话push
            /* Push new field/value pair onto the tail of the ziplist */
            zl = ziplistPush(zl, field->ptr, sdslen(field->ptr), ZIPLIST_TAIL);
            zl = ziplistPush(zl, value->ptr, sdslen(value->ptr), ZIPLIST_TAIL);
        }
        o->ptr = zl;

        //由于前面有操作让recount+1,而ziplist实际并没有共享该对象,而只是使用了对应的值,故-1
        decrRefCount(field);
        decrRefCount(value);

        //判断是否需要类型转换
        if (hashTypeLength(o) > server.hash_max_ziplist_entries)
            hashTypeConvert(o, REDIS_ENCODING_HT);
    } else if (o->encoding == REDIS_ENCODING_HT) {//哈希表
        if (dictReplace(o->ptr, field, value)) { /* Insert */
            incrRefCount(field);
        } else { /* Update */
            update = 1;
        }
        incrRefCount(value);
    } else {
        redisPanic("Unknown hash encoding");
    }
    return update;
}

 

ZRANK / ZREVRANK命令

//ZRANKE
void zrankCommand(redisClient *c) {
    zrankGenericCommand(c, 0);
}

//ZREVRANK
void zrevrankCommand(redisClient *c) {
    zrankGenericCommand(c, 1);
}
void zrankGenericCommand(redisClient *c, int reverse) {
    robj *key = c->argv[1];//键
    robj *ele = c->argv[2];//需要计算排名的成员
    robj *zobj;
    unsigned long llen;
    unsigned long rank;

    //...

    llen = zsetLength(zobj);

    if (zobj->encoding == REDIS_ENCODING_ZIPLIST) {
        //ziplist编码的情况
        unsigned char *zl = zobj->ptr;
        unsigned char *eptr, *sptr;

        eptr = ziplistIndex(zl,0);
        sptr = ziplistNext(zl,eptr);

        rank = 1;
        while(eptr != NULL) {
            if (ziplistCompare(eptr,ele->ptr,sdslen(ele->ptr))) //相同返回1
                break;
            rank++; //每往后一个节点,rank+1
            zzlNext(zl,&eptr,&sptr);

        //返回值
        if (eptr != NULL) {
            if (reverse)
                addReplyLongLong(c,llen-rank);
            else
                addReplyLongLong(c,rank-1);
        } else {
            addReply(c,shared.nullbulk);
        }

    } else if (zobj->encoding == REDIS_ENCODING_SKIPLIST) {
        //skiplist编码的情况
        zset *zs = zobj->ptr;
        zskiplist *zsl = zs->zsl;
        dictEntry *de;
        double score;

        ele = c->argv[2] = tryObjectEncoding(c->argv[2]);

        //先在字典中找出该成员对应的分值
        de = dictFind(zs->dict,ele);
        if (de != NULL) {
            score = *(double*)dictGetVal(de);

            //在跳跃表中计算对应的分值的排名
            rank = zslGetRank(zsl,score,ele);
            redisAssertWithInfo(c,ele,rank); /* Existing elements always have a rank. */
            if (reverse)
                addReplyLongLong(c,llen-rank);
            else
                addReplyLongLong(c,rank-1);
        } else {
            addReply(c,shared.nullbulk);
        }
    } else {
        redisPanic("Unknown sorted set encoding");
    }
}

zslGetRank:是跳跃表的一个API

unsigned long zslGetRank(zskiplist *zsl, double score, robj *o) {
    zskiplistNode *x;
    unsigned long rank = 0;
    int i;

    x = zsl->header;
    for (i = zsl->level-1; i >= 0; i--) { //在头节点中逐层往下找
        while (x->level[i].forward &&
            (x->level[i].forward->score < score || //该节点的forward的分值小于目标分值
                (x->level[i].forward->score == score && //或目标与该节点的forward相同
                compareStringObjects(x->level[i].forward->obj,o) <= 0))) {
            rank += x->level[i].span; //rank += span
            x = x->level[i].forward; //继续由forward向前移动
        }

        /* x might be equal to zsl->header, so test if obj is non-NULL */
        if (x->obj && equalStringObjects(x->obj,o)) {
            return rank;
        }
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值