Redis列表对象之linkedlist和ziplist实现原理分析
前言
上一篇我们分析了字符串对象的底层存储结构SDS,那么这一篇我们继续分析Redis中5种常用数据类型的第2种基本数据类列表对象
的底层存储结构。
列表对象
Redis3.2之前,列表对象其底层存储结构可以有两种,即:linkedlist
和ziplist
,而在Redis 3.2之后,列表对象底层存储结构优化成为了另一种:quicklist
。而quicklist
可以认为是linkedlist
和ziplist
的结合体。
列表内部使用哪一种类型也是通过编码来进行区分:
编码属性 | 描述 | object encoding命令返回值 |
---|---|---|
OBJ_ENCODING_LINKEDLIST | 使用linkedlist实现列表对象 | linkedlist |
OBJ_ENCODING_ZIPLIST | 使用ziplist实现列表对象 | ziplist |
OBJ_ENCODING_QUICKLIST | 使用quicklist实现列表对象 | quicklist |
linkedlist
linkedlist
是一个双向列表,每个节点都会存储指向上一个节点和指向下一个节点的指针。linkedlist
因为每个节点的空间是不连续的,所以可能会造成过多的空间碎片。
linkedlist存储结构
链表中每一个节点都是一个listNode
对象(源码adlist.h内),不过需要注意的是,列表中的value其实也是一个字符串对象,后面我们介绍的其他几种数据类型其内部最终也是会嵌套字符串对象:
typedef struct listNode {
struct listNode *prev;//前一个节点
struct listNode *next;//后一个节点
void *value;//值(字符串对象)
} listNode;
然后会将其再进行封装成为一个list对象(源码adlist.h内):
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;
Redis中对linkedlist
的访问是以NULL值为终点的,因为head节点的prev节点为NULL,tail节点的next节点为NULL。
所以,同样的,在Redis3.2之前我们可以得到如下简图:
PS:想要详细了解dictEntry
和redisObject
对象以及编码相关知识的的可以点击这里。
ziplist
ziplist
是为了节省内存而开发的一种压缩列表数据结构,后面讲述的哈希数据类型底层也用到了ziplist
。
ziplist
是由一系列特殊编码的连续内存块组成的顺序型数据结构,一个ziplist
可以包含任意多个entry
,而每一个entry
又可以保存一个字节数组或者一个整数值。
ziplist和linkedlist最大的区别是ziplist不存储指向上一个节点和下一个节点的指针,存储的是上一个节点的长度和当前节点的长度,牺牲了部分读写性能来换取高效的内存利用率,是一种时间换空间的思想。
ziplist
适用于字段个数少和字段值少的场景。
ziplist存储结构
ziplist
的组成结构为:
<zlbytes