列表数据类型是Redis
里很是经常使用的类型,当咱们想用一个键关联一组对象时就能够用列表数据类型来存储。列表的功能并不复杂,如今就来看看Redis
是怎么实现列表功能的。html
列表的编码一共有三种:node
OBJ_ENCODING_ZIPLIST
压缩列表OBJ_ENCODING_QUICKLIST
编码为ziplist的快速列表OBJ_ENCODING_LINKEDLIST
再也不使用的旧列表,使用双端链表其中OBJ_ENCODING_LINKEDLIST
编码以下面代码所示,已经被标记为不使用了。bash
#define OBJ_ENCODING_LINKEDLIST 4 /* No longer used: old list encoding. */
复制代码
而OBJ_ENCODING_ZIPLIST
编码虽然有定义建立函数,可是我下载了源码而后全文搜索也没找到哪里有调用这个函数,也已经不使用了。数据结构
robj *createZiplistObject(void) {
unsigned char *zl = ziplistNew();
robj *o = createObject(OBJ_LIST,zl);
o->encoding = OBJ_ENCODING_ZIPLIST;
return o;
}
复制代码
关于ziplist
的实现能够查看笔记函数
Redis
在版本3.2
以前列表的底层编码有OBJ_ENCODING_LINKEDLIST
和OBJ_ENCODING_ZIPLIST
两种,OBJ_ENCODING_LINKEDLIST
是在元素比较大且数量比较多的状况下使用的。ui
/* Node, List, and Iterator are the only data structures used currently. */
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;
复制代码
能够看到OBJ_ENCODING_LINKEDLIST
定义了一个list
和listNode
结构。list
结构引用了首尾两个节点,这样就能够快速的在首尾编辑节点。listNode
结构引用了先后两个节点,实现了双端链表。this
总共三种编码,有两种编码没有用到,剩下的是就是列表的主角编码OBJ_ENCODING_LINKEDLIST
了。quicklist
的定义以下:编码
/* quicklist is a 40 byte struct (on 64-bit systems) describing a quicklist. * 'count' is the number of total entries. * 'len' is the number of quicklist nodes. * 'compress' is: -1 if compression disabled, otherwise it's the number * of quicklistNodes to leave uncompressed at ends of quicklist. * 'fill' is the user-requested (or default) fill factor. */
typedef struct quicklist {
quicklistNode *head; //指向列表的头节点
quicklistNode *tail; //指向列表的尾节点
unsigned long count; // 存储的元素总和
unsigned long len; // 节点的数量
int fill : 16; // 节点的大小设置
unsigned int compress : 16; // 节点压缩深度设置;0=off;1表示quicklist两端各有一个节点不压缩,中间的节点压缩
} quicklist;
复制代码
看起来和OBJ_ENCODING_LINKEDLIST
的list
没什么区别,重点在于quicklistNode
了。spa
/* quicklistNode is a 32 byte struct describing a ziplist for a quicklist. * We use bit fields keep the quicklistNode at 32 bytes. * count: 16 bits, max 65536 (max zl bytes is 65k, so max count actually < 32k). * encoding: 2 bits, RAW=1, LZF=2. * container: 2 bits, NONE=1, ZIPLIST=2. * recompress: 1 bit, bool, true if node is temporarry decompressed for usage. * attempted_compress: 1 bit, boolean, used for verifying during testing. * extra: 10 bits, free for future use; pads out the remainder of 32 bits */
typedef struct quicklistNode {
struct quicklistNode *prev; // 前一个节点
struct quicklistNode *next; // 后一个节点
unsigned char *zl; // 节点存储的值指针
unsigned int sz; // ziplist的大小
unsigned int count : 16; // ziplist中的数据项个数
unsigned int encoding : 2; /* RAW==1 or LZF==2 */
unsigned int container : 2; /* NONE==1 or ZIPLIST==2 */
unsigned int recompress : 1; /* was this node previous compressed? */
unsigned int attempted_compress : 1; /* node can't compress; too small */
unsigned int extra : 10; /* more bits to steal for future usage */
} quicklistNode;
复制代码
能够看到quicklistNode
最神秘的就是存储值的结构了,quicklistNode
默认使用ziplist
实现。下面是quicklist
添加元素的解析:.net
/* Add new entry to head node of quicklist.
*
* Returns 0 if used existing head.
* Returns 1 if new head created. */
int quicklistPushHead(quicklist *quicklist, void *value, size_t sz) {
# 首先取出旧头节点
quicklistNode *orig_head = quicklist->head;
# 判断旧头节点的ziplist加上新元素后的大小是否超过限制,这个限制取决于quicklist结构的fill字段
if (likely(
_quicklistNodeAllowInsert(quicklist->head, quicklist->fill, sz))) {
# 没有超过限制就直接将元素加入到旧头节点的ziplist中
quicklist->head->zl =
ziplistPush(quicklist->head->zl, value, sz, ZIPLIST_HEAD);
quicklistNodeUpdateSz(quicklist->head);
} else {
# 超过限制就新建一个node
quicklistNode *node = quicklistCreateNode();
# 元素加入到新节点的ziplist中
node->zl = ziplistPush(ziplistNew(), value, sz, ZIPLIST_HEAD);
quicklistNodeUpdateSz(node);
# 并成为新的头节点
_quicklistInsertNodeBefore(quicklist, quicklist->head, node);
}
# 更新元素数量
quicklist->count++;
quicklist->head->count++;
# 返回是否有新节点建立
return (orig_head != quicklist->head);
}
复制代码
Redis
在版本3.2
以前分别在两种状况下使用OBJ_ENCODING_LINKEDLIST
和OBJ_ENCODING_ZIPLIST
两种编码。OBJ_ENCODING_LINKEDLIST
操做简单复杂度低可是内存占用高,OBJ_ENCODING_ZIPLIST
则正好相反。后面Redis
就实现了OBJ_ENCODING_QUICKLIST
编码,融合了OBJ_ENCODING_LINKEDLIST
和OBJ_ENCODING_ZIPLIST
两种编码的优点,成为了列表的惟一指定编码。