SDS 不同于 C 字符串。C 字符串本身是一个以双引号括起来,以空字符’\0’结尾的字符序 列。但 SDS 是一个结构体,定义在 Redis 安装目录下的 src/sds.h 中:
struct sdshdr {
// 字节数组,用于保存字符串
char buf[];
// buf[]中已使用字节数量,称为 SDS 的长度
int len;
// buf[]中尚未使用的字节数量
int free;
}

通过以上结构可以看出,SDS 的 buf 值实际是一个 C 字符串,包含空字符’\0’共占 6 个字 节。**但 SDS 的 len 是不包含空字符’\0’的。
**
对于 C 字符串,若要获取其长度,则必须要通过遍历整个字符串才可获取到的。对于超 长字符串的遍历,会成为系统的性能瓶颈。 SDS 结构体中直接就存放着字符串的长度数据
SDS 采用了空间预分配策略与惰性空间释放策略来避免内存再分配问题。
空间预分配策略是指,每次 SDS 进行空间扩展时,程序不但为其分配所需的空间,还会 为其分配**额外的未使用空间,以减少内存再分配次数。**而额外分配的未使用空间大小取决于 空间扩展后 SDS 的 len 属性值。
SDS 对于空间释放采用的是惰性空间释放策略。
zipList,通常称为压缩列表,是一个经过特殊编码的用于存储字符串或整数的双向链表。 其底层数据结构由三部分构成:**head、entries 与 end。**这三部分在内存上是连续存放的。 
skipList,跳跃列表,简称跳表,是一种**随机化的数据结构,基于并联的链表,**实现简单, 查找效率较高。简单来说跳表也是链表的一种,只不过它在链表的基础上增加了跳跃功能。 也正是这个跳跃功能,使得在查找元素时,能够提供较高的效率。
假设有一个带头尾结点的有序链表。

为了提升查找效率,在偶数结点上增加一个指针,让其指向下一个偶数结点。

这样所有偶数结点就连成了一个新的链表(简称高层链表),当然,高层链表包含的节 点个数只是原来链表的一半。此时再想查找某个数据时,先沿着高层链表进行查找。当遇到 第一个比待查数据大的节点时,立即从该大节点的前一个节点回到原链表中进行查找。

问题:这种对链表分层级的方式从原理上看确实提升了查找效率,但在实际操作时就出现了问 题:由于固定序号的元素拥有固定层级,所以列表元素出现增加或删除的情况下,会导致列表整体元素层级大调整,但这样势必会大大降低系统性能。
为了避免前面的问题,skipList 采用了随机分配层级方式。即在确定了总层级后,每添 加一个新的元素时会自动为其随机分配一个层级。这种随机性就解决了节点序号与层级间的 固定关系问题。

List的底层实现: quickList,快速列表,quickList 本身是一个双向无循环链表,它的每一个节点都是一个zipList。
quickList 本质上是 zipList 和 linkedList 的混合体。其将 linkedList 按段切分,每一段使用 zipList 来紧凑存储若干真正的数据元素,多个 zipList 之间使用双向指针串接起来。当然, 对于每个 zipList 中最多可存放多大容量的数据元素,在配置文件中通过 list-max-ziplist-size属性可以指定。
