redis源码解读3

接下来,我们看看列表,对应的内部编码有

ziplist
linkedlist
复制代码

常用的命令有:

rpush/lpush:lpushCommand/rpushCommand
lrange:lrangeCommand
lpop/rpop:lpopCommand/rpopCommand
blpop/brpop:blpopCommand/brpopCommand
复制代码

既然有两种内部编码,那么问题来了,分别在什么条件下对应哪种编码呢?

列表个数小于list-max-ziplist-entries配置(512),同时列表中每个元素的值都小于list-max-ziplist-value配置(64字节),
会使用ziplist这种编码,反之为linkedlist。
复制代码

那么这两种内部编码有什么区别呢?

ziplist会减少内存的使用。
复制代码

接下来,我们先分开研究这两种编码,以便更详细的对比他们。 首先我们看看linkedlist的源码实现,在adlist.h、adlist.c中

typedef struct listNode {
    struct listNode *prev;
    struct listNode *next;
    void *value;
} listNode;

typedef struct list {
    listNode *head;
    listNode *tail;
    void *(*dup)(void *ptr);
    void (*free)(void *ptr);
    int (*match)(void *ptr, void *key);
    unsigned long len;
} list;

对应命令的具体实现在t_list.c中。

void pushGenericCommand(redisClient *c, int where) {
    int j, waiting = 0, pushed = 0;
    robj *lobj = lookupKeyWrite(c->db,c->argv[1]);

    if (lobj && lobj->type != REDIS_LIST) {
        addReply(c,shared.wrongtypeerr);
        return;
    }

    for (j = 2; j < c->argc; j++) {
        c->argv[j] = tryObjectEncoding(c->argv[j]);
        if (!lobj) {
            lobj = createZiplistObject();
            dbAdd(c->db,c->argv[1],lobj);
        }
        listTypePush(lobj,c->argv[j],where);
        pushed++;
    }
    addReplyLongLong(c, waiting + (lobj ? listTypeLength(lobj) : 0));
    if (pushed) {
        char *event = (where == REDIS_HEAD) ? "lpush" : "rpush";

        signalModifiedKey(c->db,c->argv[1]);
        notifyKeyspaceEvent(REDIS_NOTIFY_LIST,event,c->argv[1],c->db->id);
    }
    server.dirty += pushed;
}

void lpushCommand(redisClient *c) {
    pushGenericCommand(c,REDIS_HEAD);
}

void rpushCommand(redisClient *c) {
    pushGenericCommand(c,REDIS_TAIL);
}
复制代码

链表的实现就结束了,下来我们再看看ziplist的源码实现。 在这问一句?redis为什么要开发ziplist这种实现呢?

节约内存!!!
是由一系列特殊编码的连续内存块组成的顺序型数据结构。

typedef struct zlentry {
    unsigned int prevrawlensize, prevrawlen;
    unsigned int lensize, len;
    unsigned int headersize;
    unsigned char encoding;
    unsigned char *p;
} zlentry;

db.c中
robj *lookupKeyWrite(redisDb *db, robj *key) {
    expireIfNeeded(db,key);
    return lookupKey(db,key);
}

int expireIfNeeded(redisDb *db, robj *key) {
    mstime_t when = getExpire(db,key);
    mstime_t now;

    if (when < 0) return 0; 

    if (server.loading) return 0;

    now = server.lua_caller ? server.lua_time_start : mstime();

    if (server.masterhost != NULL) return now > when;

    if (now <= when) return 0;

    server.stat_expiredkeys++;
    propagateExpire(db,key);
    notifyKeyspaceEvent(REDIS_NOTIFY_EXPIRED,
        "expired",key,db->id);
    return dbDelete(db,key);
}

robj *lookupKey(redisDb *db, robj *key) {
    dictEntry *de = dictFind(db->dict,key->ptr);
    if (de) {
        robj *val = dictGetVal(de);

        if (server.rdb_child_pid == -1 && server.aof_child_pid == -1)
            val->lru = server.lruclock;
        return val;
    } else {
        return NULL;
    }
}

下面以一幅图说明一下列表对象的push流程。
复制代码

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

你可能感兴趣的:(redis源码解读3)