你知道 Redis数据结构底层实现吗?一文详解,彻底弄懂( 三 )

升级的好处是什么呢?

  1. 提高了整数集合的灵活性 。
  2. 尽可能节约内存(能用小的就不用大的) 。
5.2 不支持降级
按照上面的例子,如果我把65535又删掉,encoding会不会又回到Int16呢,答案是不会的 。官方没有给出理由,我觉得应该是降低性能消耗吧,毕竟调整一次是O(N)的时间复杂度 。
6. 压缩列表(ziplist)
ziplist是redis为了节约内存而开发的顺序型数据结构 。它被用在列表键和哈希键中 。一般用于小数据存储 。
引用https://segmentfault.com/a/1190000016901154中的两个图:
你知道 Redis数据结构底层实现吗?一文详解,彻底弄懂

文章插图
 

你知道 Redis数据结构底层实现吗?一文详解,彻底弄懂

文章插图
 
6.1 源码
ziplist没有明确定义结构体,这里只作大概的演示 。
typedef struct entry { /*前一个元素长度需要空间和前一个元素长度*/ unsigned int prevlengh; /*元素内容编码*/ unsigned char encoding; /*元素实际内容*/ unsigned char *data;}zlentry;tpedef struct ziplist{ /*ziplist分配的内存大小*/ uint32_t zlbytes; /*达到尾部的偏移量*/ uint32_t zltail; /*存储元素实体个数*/ uint16_t zllen; /*存储内容实体元素*/ unsigned char* entry[]; /*尾部标识*/ unsigned char zlend;}ziplist;第一次看可能会特别蒙蔽,你细细的把我这段话看完就一定能懂 。
Entry的分析
entry结构体里面有三个重要的字段:
  1. previous_entry_length: 这个字段记录了ziplist中前一个节点的长度,什么意思?就是说通过该属性可以进行指针运算达到表尾向表头遍历,这个字段还有一个大问题下面会讲 。
  2. encoding:记录了数据类型(int16? string?)和长度 。
  3. data/content: 记录数据 。
连锁更新
previous_entry_length字段的分析
上面有说到,previous_entry_length这个字段存放上个节点的长度,那默认长度给分配多少呢?redis是这样分的,如果前节点长度小于254,就分配1字节,大于的话分配5字节,那问题就来了 。
如果前一个节点的长度刚开始小于254字节,后来大于254,那不就存放不下了吗? 这就涉及到previous_entry_length的更新,但是改一个肯定不行阿,后面的节点内存信息都需要改 。所以就需要重新分配内存,然后连锁更新包括该受影响节点后面的所有节点 。
除了增加新节点会引发连锁更新、删除节点也会触发 。
7. 快速列表(quicklist)
一个由ziplist组成的双向链表 。但是一个quicklist可以有多个quicklist节点,它很像B树的存储方式 。是在redis3.2版本中新加的数据结构,用在列表的底层实现 。

你知道 Redis数据结构底层实现吗?一文详解,彻底弄懂

文章插图
 
结构体源码
表头结构:
typedef struct quicklist { //指向头部(最左边)quicklist节点的指针 quicklistNode *head;//指向尾部(最右边)quicklist节点的指针 quicklistNode *tail;//ziplist中的entry节点计数器 unsigned long count; /* total count of all entries in all ziplists *///quicklist的quicklistNode节点计数器 unsigned int len; /* number of quicklistNodes *///保存ziplist的大小,配置文件设定,占16bits int fill : 16; /* fill factor for individual nodes *///保存压缩程度值,配置文件设定,占16bits,0表示不压缩 unsigned int compress : 16; /* depth of end nodes not to compress;0=off */} quicklist;quicklist节点结构:
typedef struct quicklistNode { struct quicklistNode *prev; //前驱节点指针 struct quicklistNode *next; //后继节点指针//不设置压缩数据参数recompress时指向一个ziplist结构 //设置压缩数据参数recompress指向quicklistLZF结构 unsigned char *zl;//压缩列表ziplist的总长度 unsigned int sz; /* ziplist size in bytes *///ziplist中包的节点数,占16 bits长度 unsigned int count : 16; /* count of items in ziplist *///表示是否采用了LZF压缩算法压缩quicklist节点,1表示压缩过,2表示没压缩,占2 bits长度 unsigned int encoding : 2; /* RAW==1 or LZF==2 *///表示一个quicklistNode节点是否采用ziplist结构保存数据,2表示压缩了,1表示没压缩,默认是2,占2bits长度 unsigned int container : 2; /* NONE==1 or ZIPLIST==2 *///标记quicklist节点的ziplist之前是否被解压缩过,占1bit长度 //如果recompress为1,则等待被再次压缩 unsigned int recompress : 1; /* was this node previous compressed? *///测试时使用 unsigned int attempted_compress : 1; /* node can't compress; too small *///额外扩展位,占10bits长度 unsigned int extra : 10; /* more bits to steal for future usage */} quicklistNode;相关配置


推荐阅读