Redis笔记(二)- 基础数据结构_List

概括

Redis中List的结构分两种。第一种是一个双向链表的结构,这个我们都清楚吧,链表比较适合插入和删除操作,而不适合做检索,如果非要获取链表中的元素,则时间复杂度将是为O(n)。第二种结构粗略的来看就是一个数组,因为在数据不大的情况下,前后指针对整个链表的负担实在太重,所以就去掉了指针,干脆用数组来存储。而Redis中List真正使用的则是链表和数组的一种结合体(有没有HashMap的感觉),打个比方就是这个链表(linkedlist )被分成很多段,每一段中的节点元素都去掉了前后指针退化为一个数组(ziplist),而整个List看上去就是一个个ziplist组成的linkedlist ,这个list叫做quicklist(不知道这种设计思想有没有让你想起跳表)。


image.png

压缩列表 ziplist

struct ziplist {
    int32 zlbytes; // 整个压缩列表占用字节数
    int32 zltail_offset; // 最后一个元素距离压缩列表起始位置的偏移量,用于快速定位到最后一个节点
    int16 zllength; // 元素个数
    T[] entries; // 元素内容列表,紧凑的连续内存空间
    int8 zlend; // 标志压缩列表的结束,值恒为 0xFF
}
image.png

每一个entry的结构:

struct entry {
    int prevlen; // 前一个 entry 的字节长度
    int encoding; // 元素类型编码
    optional byte[] content; // 元素内容
}

prevlen这个参数是用于倒序遍历的,它是变长的,当前一个entry长度小于 254 时,长度为一个字节;超出254时使用5个字节表示。这样会产生一个什么问题呢?当前entry的prevlen的字节长度取决于前一个entry 的总长度(entry+encoding+content),假如前一个entry的content更新了,那当前entry的prevlen的字节长度也可能会变,影响的是当前entry的总长度,当前entry的总长度又会影响下一个entry的prevlen......会产生级联更新的问题,这可能是内存做到极致优化后出现的一些可以承担的小问题。
当然,对于数组来讲,大家都知道,新增元素也可能会因为无法申请到连续的内存空间而进行一次数组的复制,所以一个ziplist不易存储过多的元素。

快速列表 quicklist

链表
struct quicklist {
    quicklistNode* head;
    quicklistNode* tail;
    long count; // 元素总数
    int nodes; // ziplist 节点的个数
    int compressDepth; // LZF 算法压缩深度
    ...
}

链表中的节点
struct quicklistNode {
    quicklistNode* prev;
    quicklistNode* next;
    ziplist* zl; // 指向压缩列表
    int32 size; // ziplist 的字节总数
    int16 count; // ziplist 中的元素数量
    int2 encoding; // 存储形式 2bit,原生字节数组还是 LZF 压缩存储
    ...
}

quicklist 中有个压缩深度的参数,因为redis中list主要拿来当做队列使用,队首和队尾都可以弹出元素,所以可以不用压缩队首和队尾的元素,这个参数书大小,就是,队尾和队首不需要压缩的数量。

你可能感兴趣的:(Redis笔记(二)- 基础数据结构_List)