列表(list)用来存储多个有序的元素; 一个列表可以存储2^32-1个元素。
Redis中的列表支持两端插入和弹出,并可以获得指定位置(或范围)的元素,可以充当数组、队列、栈等。
1 内部编码
列表的内部编码可以是压缩列表(ziplist)或双端链表(linkedlist)
-
双端链表
与双链表定义一致,引入了链表节点,并在此基础上增加头尾节点构建双端链表
链表节点listNode如下定义:
/* 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;
// 节点数量
unsigned long len;
// 复制函数
void *(*dup)(void *ptr);
// 释放函数
void (*free)(void *ptr);
// 比对函数
int (*match)(void *ptr, void *key);
} list;
双端链表结构如下图所示:
通过图中可以看出,双端链表同时保存了表头指针和表尾指针,并且每个节点都有指向前和指向后的指针;链表中保存了列表的长度;dup、free和match为节点值设置类型特定函数,所以链表可以用于保存各种不同类型的值。而链表中每个节点指向的是type为字符串的redisObject。
-
压缩列表
压缩列表是Redis为了节约内存而开发的,是由一系列特殊编码的连续内存块(而不是像双端链表一样每个节点是指针)组成的顺序型数据结构。
-
zlbytes
记录整个压缩列表占用的内存字节数,在对压缩列表进行内存重分配或计算 -
zlend
的位置时使用 -
zltail
记录压缩列表尾节点距离压缩列表的起始地址有多少字节,通过这个偏移量,可以直接确定尾节点的位置 -
zllen
记录压缩列表包含的节点数量 -
entryX
表示各种节点,数量和长度不一定 -
zlend
用于标记压缩列表的末端。
如图,如果有一个指针p指向该压缩列表,则尾巴节点的长度就是指针加上偏移量179(十六进制0xb3=16*11+3=179),列表的长度zllen为5,表示压缩列表包含5个节点。zlbytes为0xd2表示压缩列表的总长为210字节。
由上可知,每个压缩列表的节点可以保存一个字节数组或者一个整数值,那么每个节点肯定也有自己的结构。
与双端链表相比,压缩列表可以节省内存空间,但是进行修改或增删操作时,复杂度较高
因此当节点数量较少时,可以使用压缩列表;但是节点数量多时,还是使用双端链表划算
压缩列表不仅用于实现列表,也用于实现哈希、有序列表;使用非常广泛。