咱们继续接着上一篇博客,今天来看看内存映射数据结构。算法
上篇咱们讲了内部数据结构,虽然内部数据结构很是强大,可是建立一系列完整的数据结构自己也是一件至关耗费时间的工做,当一个对象包含的元素数量并很少,或者元素自己的体积并不大时,使用代价高昂的内部数据结构并非最好的办法。所以咱们会用内存映射数据结构来代替内部数据结构。数组
内存映射数据结构是一系列通过特殊编码的字节序列,建立他们所消耗的内存一般比做用相似的内部数据结构要少得多,若是使用得当,内存映射数据结构能够为用户节省大量的内存。不过,内存映射数据结构的编码和操做方式要比内部数据结构复杂的多,因此内存映射数据结构所占用的CPU时间会比做用相似的内部结构要多。数据结构
2.1整数集合函数
整数集合(intset)用于有序、无重复地保存多个整数值,他会根据元素的值,自动选择该用什么长度的整数类型来保存元素。ui
2.1.1 整数集合的应用编码
intset是集合键的底层实现之一,若是一个集合知足:spa
* 值保存着整数元素;指针
* 元素的数量很少;code
那么就会使用intset来保存集合元素。对象
2.1.2 数据结构和主要操做
typedef struct intset { // 保存元素所使用的类型的长度 uint32_t encoding; // 元素个数 uint32_t length; // 保存元素的数组 int8_t contents[]; } intset;
encoding 的值能够是如下三个常量的其中一个(定义位于intset.c ):
#define INTSET_ENC_INT16 (sizeof(int16_t))
#define INTSET_ENC_INT32 (sizeof(int32_t))
#define INTSET_ENC_INT64 (sizeof(int64_t))
contents数组是实际保存元素的地方,数组有一下两个特性:
* 没有重复元素;
* 从小到大排序;
contents的 int8_t类型只是做为一个占位符使用,intset不使用int8_t类型保存任何元素。新增元素默认的encoding是int16_t,当添加的新元素不适合于当前intset的编码类型时,intset集合将会进行升级。
2.1.3 小结
* intset用于有序、无重复的保存多个整数值。他会根据元素的值,自动选择该用什么长度的整数类型来保存元素;
* 当一个位长度更长的整数值添加到intset时,须要对intset进行升级,新intset中的每一个元素的位长度都等于新添加值的位长度,但原有元素的值不变;
* 升级会引发整个intset进行内存重分配,并移动集合中的全部元素,这个操做的复杂度为O(N);
* intset只支持升级,不支持降级;
* intset是有序的,程序使用二分法查找算法来实现查找操做,复杂度为O(lgN);
2.2 压缩列表
ziplist 是由一系列特殊编码的内存块构成的列表,一个ziplist 能够包含多个节点(entry),每一个节点能够保存一个长度受限的字符数组(不以\0结尾的char数组)或者整数,包括:
• 字符数组
– 长度小于等于63 (26 - 1)字节的字符数组
– 长度小于等于16383 (214 - 1)字节的字符数组
– 长度小于等于4294967295 (232 - 1)字节的字符数组
• 整数
– 4 位长,介于0 至12 之间的无符号整数
– 1 字节长,有符号整数
– 3 字节长,有符号整数
– int16_t 类型整数
– int32_t 类型整数
– int64_t 类型整数
由于ziplist节约内存的性质,它被哈希键、列表建和有序集合键做为初始化的底层实现来使用。
2.2.1 ziplist的结构:
由于ziplist header 部分的长度老是固定的(4 字节+ 4 字节+ 2 字节),所以将指针移动到表头节点的复杂度为常数时间;除此以外,由于表尾节点的地址能够经过zltail 计算得出,所以将指针移动到表尾节点的复杂度也为常数时间。
由于ziplist 由连续的内存块构成,在最坏状况下,当ziplistPush 、ziplistDelete 这类对节点进行增长或删除的函数以后,程序须要执行一种称为连锁更新的动做来维持ziplist 结构自己的性质,因此这些函数的最坏复杂度都为O(N2) 。不过,由于这种最坏状况出现的几率并不高,因此大能够放心使用ziplist ,而没必要太担忧出现最坏状况。
2.2.2 节点的构成:
pre_entry_length:记录了前一个节点的长度,经过这个值,能够进行指针计算,从而跳转到上一个节点。(注:若前一个节点的长度小于254字节,则使用一个字节保存pre_entry_length的值,若大于等于254,则使用5个字节保存,其中第一个字节保存254,后4个字节保存前一个节点的实际长度);
encoding:记录了content的数据类型,长度为2个bit,它的值能够是00、0一、10和11(其中00、01和10表示Content中保存着字符数组;11表示content中保存着整数);
length:记录了content的数据长度;
content:保存着节点的内容
2.2.3 小结:
* ziplist是由一系列特殊编码的内存块构成的列表,它能够保存字符数组或整数值,它仍是哈希键、列表键和有序集合键的底层实现之一。
* 添加和删除ziplist节点有可能会引发连锁更新,所以,添加和删除操做的最坏复杂度为O(N2),不过,由于连锁更新的出现几率并不高,因此通常能够将添加和删除操做的复杂度视为O(N)。