简介:大家好,我是冰点,从业11年,目前在物流独角兽企业从事技术管理和架构设计方面工作,之前的把博客作为技术流水账在写。现在准备把多年的积累整理一下,成体系的分享给大家,也算是对多年开发生涯的总结。如果你在工作和学习中遇到问题也可反馈给我(iceicepip), 路漫漫其修远兮,吾将上下而求索。
️2023计划:
1. 将多年来整理的Redis学习和实践笔记整理并发布成专栏。
2. 将最近2年在groovy实践应用上的沉淀的初稿,发布成书籍。
3. 将多年来整理的MySQL学习研究笔记整理并发布成专栏。
4. 根据技术交流群答疑的问题,整理成博客文章发布分享。
压缩列表(ZipList)就是redis为了节约内存而设计开发的数据结构,并且作为列表键和哈希键的底层实现之一。Redis中的压缩列表(ZipList)是一种特殊的数据结构,用于存储一系列的连续元素。ZipList是Redis中的底层数据结构之一,常用于存储列表和哈希表等数据类型的底层实现。在本文中,我们将深入了解Redis中的压缩列表,包括ZipList的结构和操作等。
Redis中的压缩列表(ZipList)是由一系列的节点(entry)组成的。每个节点可以是一个字节数组(byte array)、一个整数或者一个指针。在ZipList中,每个节点的大小是不固定的,取决于节点所包含的数据类型和数据大小。ZipList中节点的个数也是不固定的,可以根据需要动态增加或减少。
ZipList的结构如下图所示:
+--------+--------+--------+--------+--+-----+
| zlbytes| zltail | zllen |entry1 |..|entryN|
+--------+--------+--------+--------+--+-----+
zlbytes
字段的类型是uint32_t, 这个字段中存储的是整个ziplist所占用的内存的字节数zltail
字段的类型是uint32_t, 它指的是ziplist中最后一个entry的偏移量. 用于快速定位最后一个entry, 以快速完成pop等操作zllen
字段的类型是uint16_t, 它指的是整个ziplit中entry的数量. 这个值只占2bytes(16位): 如果ziplist中entry的数目小于65535(2的16次方), 那么该字段中存储的就是实际entry的值. 若等于或超过65535, 那么该字段的值固定为65535, 但实际数量需要一个个entry的去遍历所有entry才能得到.zlend
是一个终止字节, 其值为全F, 即0xff. ziplist保证任何情况下, 一个entry的首字节都不会是255其中,zlbytes是压缩列表的长度(包括所有的字节),zltail是指向压缩列表尾部的指针,zllen是压缩列表中节点的个数,entry1到entryN是压缩列表中的所有节点。
###节点结构
在压缩列表中,每个节点的结构如下:
+--------+--------+
| prevlen| encoding| data |
+--------+--------+
prevlen是前一个节点的长度(单位为字节),encoding是数据的编码方式,data是节点的实际数据。在压缩列表中,prevlen和encoding都是可选的。当节点的前一个节点的长度小于254字节时,prevlen字段被省略,encoding字段存储在数据之前,否则prevlen字段占用5个字节,encoding字段存储在prevlen后面的5个字节中。
根据不同的数据类型,压缩列表中节点的编码方式也有所不同,下面是常用的节点编码方式:
+--------+--------+---------------+
| prevlen| 0xc000 | length |
+--------+--------+---------------+
| data |
+---------------------------------+
其中,0xc000是一个特殊的编码方式,用于标识节点存储的是字节数组。length是字节数组的长度,data是字节数组的实际数据。
+--------+--------+--------+
| prevlen| int | int |
+--------+--------+--------+
其中,int是一个整数,可以是8位、16位或32位的有符号整数。
+--------+--------+--------+
| prevlen| 0x01 | ptr |
+--------+--------+--------+
其中,0x01是一个特殊的编码方式,用于标识节点存储的是指针。ptr是一个指针,可以指向任意的内存地址。
Redis中的压缩列表支持以下常用的操作:
unsigned char *zl = ziplistNew();
zl = ziplistPush(zl, s, len, ZIPLIST_TAIL);
其中,s是一个字节数组,len是字节数组的长度,ZIPLIST_TAIL表示在压缩列表的尾部添加节点。
zl = ziplistPushInt(zl, value);
其中,value是一个整数,表示在压缩列表的尾部添加整数节点。
zl = ziplistDelete(zl, &p);
其中,p是一个指向要删除的节点的指针。
unsigned char *p = ziplistIndex(zl, index);
unsigned char *entry = NULL;
unsigned int entry_len = 0;
long long entry_int = 0;
int ret = ziplistGet(p, &entry, &entry_len, &entry_int);
其中,index是节点的下标,p是指向节点的指针,entry是节点的数据(字节数组或整数),entry_len是字节数组的长度,entry_int是整数的值,ret表示节点的数据类型(字节数组或整数)。
unsigned int ziplistLen(unsigned char *zl);
以上是常用的压缩列表操作,还有其他的操作可以参考Redis源代码中的ziplist.h和ziplist.c文件。
本文详细介绍了Redis中的压缩列表(ZipList),包括ZipList的结构和操作等。压缩列表是Redis中的底层数据结构之一,常用于存储列表和哈希表等数据类型的底层实现。压缩列表具有紧凑的存储结构、支持动态增加和删除节点、支持快速的节点访问和遍历等优点,但也有节点大小不固定、不支持快速的节点插入和删除操作等缺点。在实际应用中,需要根据具体的需求选择合适的数据结构来存储数据。
如果想详细阅读源码解析推荐学习铁蕾的这篇文章
Redis内部数据结构详解(4)——ziplist