一、不同数据类型存储结构
Redis底层数据结构一共有 6 种,分别是简单动态字符串、双向链表、压缩列表、哈希表、跳表和整数数组。它们和数据类型的对应关系如下图所示:
1 数组与链表的区别
数组和链表是所有数据结构的基础,其差别在于数据在内存中分步是连续的,详情请参考:参考1: 数据结构之数组和链表的区别_Jasminexjf的博客-CSDN博客_数组与链表的区别第一题便是数据结构中的数组和链表的区别数组(Array)一、数组特点:所谓数组,就是相同数据类型的元素按一定顺序排列的集合;数组的存储区间是连续的,占用内存比较大,故空间复杂的很大。但数组的二分查找时间复杂度小,都是O(1);数组的特点是:查询简单,增加和删除困难;1.1 在内存中,数组是一块连续的区域1.2 数组需要预留空间 在使用前需要提前申请所占内存的大小,...https://blog.csdn.net/Jasminexjf/article/details/88847127
什么是ziplist
Redis官方对于ziplist的定义是(出自ziplist.c的文件头部注释):
The ziplist is a specially encoded dually linked list that is designed to be very memory efficient. It stores both strings and integer values, where integers are encoded as actual integers instead of a series of characters. It allows push and pop operations on either side of the list in O(1) time.
翻译一下就是说:ziplist是一个经过特殊编码的双向链表,它的设计目标就是为了提高存储效率。ziplist可以用于存储字符串或整数,其中整数是按真正的二进制表示进行编码的,而不是编码成字符串序列。它能以O(1)的时间复杂度在表的两端提供push
和pop
操作。
ziplist的数据结构定义
ziplist的数据结构组成是本文要讨论的重点。实际上,ziplist还是稍微有点复杂的,它复杂的地方就在于它的数据结构定义。一旦理解了数据结构,它的一些操作也就比较容易理解了。
我们接下来先从总体上介绍一下ziplist的数据结构定义,然后举一个实际的例子,通过例子来解释ziplist的构成。如果你看懂了这一部分,本文的任务就算完成了一大半了。
从宏观上看,ziplist的内存结构如下:
压缩链表存储结构:
各个部分在内存上是前后相邻的,它们分别的含义如下:
<zlbytes>
: 32bit,表示ziplist占用内存的字节总数(也包括<zlbytes>
本身占用的4个字节),在对压缩列表进行重新内存分配,或者计算zllen的位置时使用;<zltail>
: 32bit,表示ziplist表中最后一项(entry)的起始偏移量在ziplist中的起始地址有多少字节。<zltail>
的存在,使得我们可以很方便地找到最后一项(不用遍历整个ziplist),从而可以在ziplist尾端快速地执行push或pop操作。<zllen>
: 16bit, 表示ziplist中数据项(entry)的个数。zllen字段因为只有16bit,所以可以表达的最大值为2^16-1。这里需要特别注意的是,如果ziplist中数据项个数超过了16bit能表达的最大值,ziplist仍然可以来表示。那怎么表示呢?这里做了这样的规定:如果<zllen>
小于等于2^16-2(也就是不等于2^16-1),那么<zllen>
就表示ziplist中数据项的个数;否则,也就是<zllen>
等于16bit全为1的情况,那么<zllen>
就不表示数据项个数了,这时候要想知道ziplist中数据项总数,那么必须对ziplist从头到尾遍历各个数据项,才能计数出来。<entry>
: 表示真正存放数据的数据项,长度不定。一个数据项(entry)也有它自己的内部结构,这个稍后再解释。<zlend>
: ziplist最后1个字节,是一个结束标记,值固定等于255。
上面的定义中还值得注意的一点是:<zlbytes>
, <zltail>
, <zllen>
既然占据多个字节,那么在存储的时候就有大端(big endian)和小端(little endian)的区别。ziplist采取的是小端模式来存储,这在下面我们介绍具体例子的时候还会再详细解释。