接下来,我们看看列表,对应的内部编码有
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流程。
复制代码