redis源码解读6

下面我看看集合对象,内部编码也有两种:

intset
hashtable
复制代码

同样的问题,分别什么条件用哪种编码呢?

当集合中元素都是整数且元素个数小于set-max-intset-entries(512),用intset作为内部实现,减少内存的使用,反之hashtable。
复制代码

看看set有哪些常用的命令呢?

sadd:saddCommand
srem
scard
sismember
srandmember
spop
smembers
复制代码

源码实现在t_set.c中

void saddCommand(redisClient *c) {
    robj *set;
    int j, added = 0;

    set = lookupKeyWrite(c->db,c->argv[1]);
    if (set == NULL) {
        set = setTypeCreate(c->argv[2]);
        dbAdd(c->db,c->argv[1],set);
    } else {
        if (set->type != REDIS_SET) {
            addReply(c,shared.wrongtypeerr);
            return;
        }
    }

    for (j = 2; j < c->argc; j++) {
        c->argv[j] = tryObjectEncoding(c->argv[j]);
        if (setTypeAdd(set,c->argv[j])) added++;
    }
    if (added) {
        signalModifiedKey(c->db,c->argv[1]);
        notifyKeyspaceEvent(REDIS_NOTIFY_SET,"sadd",c->argv[1],c->db->id);
    }
    server.dirty += added;
    addReplyLongLong(c,added);
}

robj *setTypeCreate(robj *value) {
    if (isObjectRepresentableAsLongLong(value,NULL) == REDIS_OK)
        return createIntsetObject();
    return createSetObject();
}
int setTypeAdd(robj *subject, robj *value) {
    long long llval;
    if (subject->encoding == REDIS_ENCODING_HT) {
        if (dictAdd(subject->ptr,value,NULL) == DICT_OK) {
            incrRefCount(value);
            return 1;
        }
    } else if (subject->encoding == REDIS_ENCODING_INTSET) {
        if (isObjectRepresentableAsLongLong(value,&llval) == REDIS_OK) {
            uint8_t success = 0;
            subject->ptr = intsetAdd(subject->ptr,llval,&success);
            if (success) {
                if (intsetLen(subject->ptr) > server.set_max_intset_entries)
                    setTypeConvert(subject,REDIS_ENCODING_HT);
                return 1;
            }
        } else {
            setTypeConvert(subject,REDIS_ENCODING_HT);
            redisAssertWithInfo(NULL,value,dictAdd(subject->ptr,value,NULL) == DICT_OK);
            incrRefCount(value);
            return 1;
        }
    } else {
        redisPanic("Unknown set encoding");
    }
    return 0;
}

在intset.h和intset.c中
typedef struct intset {
    uint32_t encoding;//编码方式,int16_t、int32_t、int64_t
    uint32_t length;//集合包含元素的数量
    int8_t contents[];//保存元素的数组
} intset;

intset相对比较简单,就暂时解读到这。
接下来看看有序集合zset的实现,根据名字就知道他也是一个集合,那么他是如何实现排序的呢?
先看看几个常用的命令:
zadd
zcard
zscore
zrank/zrevrank
zrange/zrevrange

zset有两种编码
ziplist:zset的元素个数小于zset-max-ziplist-entries(128),同时每个元素的值小于zset-max-ziplist-value(64字节)
skiplist:反之

到这,我们发现,redis默认的内部编码都是为了节约内存考虑的。
先来看看跳跃表这种数据结构的实现,在redis.h中

typedef struct zset {
    dict *dict;
    zskiplist *zsl;
} zset;

typedef struct zskiplist {
    struct zskiplistNode *header, *tail;
    unsigned long length; //跳跃表的长度
    int level; //层数最大的那个节点的层数
} zskiplist;

typedef struct zskiplistNode {
    robj *obj;
    double score;
    struct zskiplistNode *backward;
    struct zskiplistLevel {
        struct zskiplistNode *forward;
        unsigned int span; //跨度,用来计算排位rank的
    } level[];
} zskiplistNode;

节点按score从小到大排序,分值相同按照成员对象obj在字典序中的大小排序
层数如何确定?
    每次创建一个新跳跃表节点的时候,程序都根据幂次定律随机生成一个介于1和32之间的值作为数组的大小。
    #define ZSKIPLIST_MAXLEVEL 32 /* Should be enough for 2^32 elements */
    #define ZSKIPLIST_P 0.25      /* Skiplist P = 1/4 */
    int zslRandomLevel(void) {
        int level = 1;
        while ((random()&0xFFFF) < (ZSKIPLIST_P * 0xFFFF))
            level += 1;
        return (level<ZSKIPLIST_MAXLEVEL) ? level : ZSKIPLIST_MAXLEVEL;
    }复制代码

转载于:https://juejin.im/post/5c8b0b0e5188257dec6b0ce1

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值