深入底层C源码 Redis核心设计原理( 五 )

【3】图示:

深入底层C源码 Redis核心设计原理

文章插图
【4】图示参数说明
zlbytes:32bit,表示ziplist占用的字节总数 。zltail:32bit,表示ziplist表中最后一项(entry)在ziplist中的偏移字节数 。通过zltail我们可以很方便地找到最后一项,从而可以在ziplist尾端快速地执行push或pop操作zlen:16bit ,  表示ziplist中数据项(entry)的个数 。entry:表示真正存放数据的数据项,长度不定zlend: ziplist最后1个字节,是一个结束标记,值固定等于255 。prerawlen: 前一个entry的数据长度 。len: entry中数据的长度data: 真实数据存储【5】说明
1.Ziplist的设计结构,保障了空间的节省与查询的高效 , 但是当出现zlentry增加或删除时,Ziplist是不能直接在原有空间上进行修改,每一次变动都需要重新开辟空间去拷贝、修改 。这样的场景下Ziplist一旦内部元素过多,将会导致性能的急剧下滑 。因此Redis 在实现上做了一层优化,当Ziplist过大时 , 会将其分割成多个Ziplist,然后再通过一个双向链表将其串联起来 。
3)quicklist 分析详解
【1】介绍
1.Redis quicklist是Redis 3.2版本以后针对链表和压缩列表进行改造的一种数据结构,是 zipList 和 linkedList 的混合体,相对于链表它压缩了内存,进一步的提高了效率 。
【2】代码展示
robj *createQuicklistObject(void) {quicklist *l = quicklistCreate();robj *o = createObject(OBJ_LIST,l);o->encoding = OBJ_ENCODING_QUICKLIST;return o;}//处于quicklist.c文件中quicklist *quicklistCreate(void) {struct quicklist *quicklist;quicklist = zmalloc(sizeof(*quicklist));quicklist->head = quicklist->tail = NULL;quicklist->len = 0;quicklist->count = 0;quicklist->compress = 0;quicklist->fill = -2;quicklist->bookmark_count = 0;return quicklist;}//处于quicklist.h文件中//quicklist 是一个 40 字节的结构(在 64 位系统上),描述了一个快速列表 。typedef struct quicklist {quicklistNode *head;//指向头节点(左侧第一个节点)的指针 。quicklistNode *tail;//指向尾节点(右侧第一个节点)的指针 。unsigned long count;// 所有 quicklistNode 节点中所有的 entry 个数unsigned long len;// quickListNode 节点个数 , 也就是 quickList 的长度int fill : QL_FILL_BITS;//单个节点的填充因子,也就是 ziplist 的大小unsigned int compress : QL_COMP_BITS;// 保存压缩成都只,配置文件设置,64位操作系统占 16bit , 6 表示压缩unsigned int bookmark_count: QL_BM_BITS;quicklistBookmark bookmarks[];} quicklist;//quicklistNode 是一个 32 字节的结构,描述了一个快速列表的 ziplist 。typedef struct quicklistNode {struct quicklistNode *prev;// 双向链表前驱节点struct quicklistNode *next;// 双向链表的后节点unsigned char *zl;//数据指针 。如果当前节点的数据没有压缩 , 那么它指向一个ziplist结构;否则,它指向一个quicklistLZF结构 。unsigned int sz;// 压缩列表 ziplist 的总长度unsigned int count : 16;// 每个 ziplist 中 entry 的个数unsigned int encoding : 2;// 表示是否采用了 LZF 压缩 quickList 节点 1 表示压缩过,2 表示没有压缩站 2bit 长度unsigned int container : 2;// 表示是否开启 ziplist 进行压缩unsigned int recompress : 1;// 表示该节点是否被压缩过unsigned int attempted_compress : 1;// 测试使用unsigned int extra : 10;// 额外拓展位,占 10bit 长度} quicklistNode;//当指定使用 lzf 压缩算法压缩 ziplist entry 节点时,quicklistNode 结构的 zl 成员执行 quicklistLZF 结构typedef struct quicklistLZF {unsigned int sz;//表示被LZF 压缩后的 ziplist 的大小char compressed[];// 压缩有的数据,柔性数组} quicklistLZF;【3】图示:
深入底层C源码 Redis核心设计原理

文章插图
【4】说明
1.通过控制ziplist 的大小 , 则很好的解决了超大ziplist 的拷贝情况下对性能的影响 。每次改动只需要针对具体的小段ziplist 进行操作 。
4)发现说明
【1】为什么不采用两个指针指向前后数据的方式,而是要采用复合的数据结构完成?
1.采用双指针的方式,那就必须赋予两个指针pre和next,一个指针占据了8byte,故两个指针就需要消耗16byte 。如果list存在大量数据,所以就需要消耗相当多的内存在指针方面(胖指针问题) 。
2.采用双链表的话数据可能会分的很散,因为指针就是采用不连续的存储空间来存储数据,容易造成大量的内存碎片 。
3.采用quicklist 和 ziplist 混合,达到减少指针消耗的空间 , 其次连续的存储空间读取起来效率高于不连续的存储空间,节省IO 。

推荐阅读