Download as pdf or txt
Download as pdf or txt
You are on page 1of 4

redis quicklist​

为什么引入quicklist​
v3.2以前,redis list对象中元素的长度比较小或者数量比较少的时候,采用ziplist来存储。当列表对
象中元素的长度比较大或者数量比较多的时候,则会转而使用双向列表linkedlist来存储。​
v3.2以后,引入了一个 quicklist 的数据结构,列表的底层都由quicklist实现, list对象使用
quicklist。​
linkedlist vs ziplist​
linkedlist优缺点​
优点: 插入,删除节点复杂度很低,简单​
缺点: 除保存数据外还需要保存prev, next两个指针,内存利用率低,双向链表的各个节点是单独的
内存块,地址不连续,节点多了容易产生内存碎片。
ziplist优缺点​
优点: 存储在一段连续的内存上,不容易产生内存碎片,内存利用率高。​
缺点: 插入和删除操作需要频繁的申请和释放内存, 同时会发生内存拷贝,数据量大时内存拷贝开销
较大。
quicklist数据结构​
1 typedef struct quicklistNode {
2 struct quicklistNode *prev;
3 struct quicklistNode *next;
4 unsigned char *zl;
5 unsigned int sz; 占用
/* ziplist 数
byte */​
6 unsigned int count : 16; /* count of items in ziplist */
7 unsigned int encoding : 2; /* RAW==1 or LZF==2 */
8 unsigned int container : 2; /* NONE==1 or ZIPLIST==2 */
9 unsigned int recompress : 1; /* was this node previous compressed? */
10 unsigned int attempted_compress : 1; /* node can't compress; too small */
11 unsigned int extra : 10; /* more bits to steal for future usage */
12 } quicklistNode;
13
14 typedef struct quicklistLZF {
15 unsigned int sz; /* LZF size in bytes*/
16 char compressed[];
17 } quicklistLZF;
18
19 typedef struct quicklist {
20 quicklistNode *head;
21 quicklistNode *tail;
22 unsigned long count; /*所有 中
ziplist entry的数量 */​
23 unsigned long len; /* quicklistNode 的数量 */​
24 int fill : 16; /* fill factor for individual nodes */
25 unsigned int compress : 16; /* depth of end nodes not to compress;0=off */
26 } quicklist;

lpush的过程​
lpush过程是从网络缓存区读到数据并将数据包装成client之后的处理过程。rpush过程与lpush类
似,只是插入的位置有LIST_HEAD改为LIST_TAIL​
1 在
/** lpushCommand, pushGenericCommand, listTypePush t_list.c 文件中*/​
2 void lpushCommand(client *c) {
3 插入的是
// LIST_HEAD​
4 pushGenericCommand(c,LIST_HEAD);
5 }
6
7 void pushGenericCommand(client *c, int where) {
8 int j, pushed = 0;
9 从 数据机构里找到要插入的
// db 地址
quicklist ​
10 robj *lobj = lookupKeyWrite(c->db,c->argv[1]);
11 判断编码格式
// ​
12 if (lobj && lobj->type != OBJ_LIST) {
13 addReply(c,shared.wrongtypeerr);
14 return;
15 }
16
17 for (j = 2; j < c->argc; j++) {
18 if (!lobj) {
19 //db 中不存在 quicklist,初始化并存储到 db​
20 lobj = createQuicklistObject();
21 quicklistSetOptions(lobj->ptr, server.list_max_ziplist_size,
22 server.list_compress_depth);
23 dbAdd(c->db,c->argv[1],lobj);
24 }
25 // 进行push过程 ​
26 listTypePush(lobj,c->argv[j],where);
27 pushed++;
28 }
29 addReplyLongLong(c, (lobj ? listTypeLength(lobj) : 0));
30 if (pushed) {
31 char *event = (where == LIST_HEAD) ? "lpush" : "rpush";
32
33 signalModifiedKey(c->db,c->argv[1]);
34 notifyKeyspaceEvent(NOTIFY_LIST,event,c->argv[1],c->db->id);
35 }
36 server.dirty += pushed;
37 }
38

39 //push quicklist 的过程 ​
40 void listTypePush(robj *subject, robj *value, int where) {
41 //编码检查 ​
42 if (subject->encoding == OBJ_ENCODING_QUICKLIST) {
43 //插入QUICKLIST_HEAD​
44 int pos = (where == LIST_HEAD) ? QUICKLIST_HEAD : QUICKLIST_TAIL;
45 //从robj数据结构中提取出要插入的value,计算value的长度​
46 value = getDecodedObject(value);
47 size_t len = sdslen(value->ptr);
48 的
//quicklist push操作 ​
49 quicklistPush(subject->ptr, value->ptr, len, pos);
50 decrRefCount(value);
51 } else {
52 serverPanic("Unknown list encoding");
53 }
54 }
55
56 /** quicklistPush, quicklistPushHead 在 文件中
quicklist.c */​
57 void quicklistPush(quicklist *quicklist, void *value, const size_t sz,
58 int where) {
59 //这里 lpush 是插入 QUICKLIST_HEAD, rpush是插入 QUICKLIST_TAIL​
60 if (where == QUICKLIST_HEAD) {
61 quicklistPushHead(quicklist, value, sz);
62 } else if (where == QUICKLIST_TAIL) {
63 quicklistPushTail(quicklist, value, sz);
64 }
65 }
66
67 int quicklistPushHead(quicklist *quicklist, void *value, size_t sz) {
68 quicklistNode *orig_head = quicklist->head;
69 判断是否// 一个 quicklist 是否满了,不满直接找到
quicklistNode 的
quicklistNode ziplist,
使用 的 操作, 满则新建
ziplist push ,使用新
quicklistNode 的 进行
quicklistNode ziplist push​
70 if (likely(
71 _quicklistNodeAllowInsert(quicklist->head, quicklist->fill, sz))) {
72 //调用 的
ziplist api​
73 quicklist->head->zl =
74 ziplistPush(quicklist->head->zl, value, sz, ZIPLIST_HEAD);
75 quicklistNodeUpdateSz(quicklist->head);
76 } else {
77 quicklistNode *node = quicklistCreateNode();
78 node->zl = ziplistPush(ziplistNew(), value, sz, ZIPLIST_HEAD);
79 //更新 的 属性
quicklistNode sz 占用字节数
(ziplist )​
80 quicklistNodeUpdateSz(node);
81 _quicklistInsertNodeBefore(quicklist, quicklist->head, node);
82 }
83 更新
// 的
quicklist entry 数量, 的
quicklist header enty数量 ​
84 quicklist->count++;
85 quicklist->head->count++;
86 return (orig_head != quicklist->head);
87 }
88
89 /** ziplistPush 在
ziplist.c */​
90 unsigned char *ziplistPush(unsigned char *zl, unsigned char *s, unsigned int
slen, int where) {
91 unsigned char *p;
92 // 计算插入位置 p​
93 p = (where == ZIPLIST_HEAD) ? ZIPLIST_ENTRY_HEAD(zl) : ZIPLIST_ENTRY_END(zl);
94 在
// redis ziplist 中有
__ziplistInsert 的解释​
95 return __ziplistInsert(zl,p,s,slen);
96 }

You might also like