redis创建像mysql表结构_Redis数据结构列表实现

双向链表linkedlist

Redis实现的是标准的双向链表。

链表节点定义:

c0a10751ff198fcbd714028974ea0254.png

bc37504444058e94a139510ef8e1a5af.png

链表定义:

e1081920dda3ce44b8145f26448e9515.png

总结链表实现:

1.每个节点有前后节点指针,且第一个节点的指针为NULL,最后一个节点的指针为NULL(无环)。

2.对双链表进行封装,链表第一个节点和最后一个节点指针,以及链表长度。

优点:

在链表两端进行push和pop操作都是O(1)。

获取链表的长度操作O(1)。

多态,可以存储c语言支持的任何数据类型,通过void *万能指针。

缺点:

内存开销比较大。每个节点上除了要保存数据之外,还要额外保存两个指针

各个节点是单独的内存块,地址不连续,节点多了容易产生内存碎片。

压缩列表转化成双向链表条件

创建新列表时 redis 默认使用 redis_encoding_ziplist 编码, 当以下任意一个条件被满足时, 列表会被转换成 redis_encoding_linkedlist 编码:

试图往列表新添加一个字符串值,且这个字符串的长度超过 server.list_max_ziplist_value (默认值为 64 )。

ziplist 包含的节点超过 server.list_max_ziplist_entries (默认值为 512 )。

注意:这两个条件是可以修改的,在 redis.conf 中

list-max-ziplist-value 64list-max-ziplist-entries 512

ziplist

压缩列表 ziplist 是为 Redis 节约内存而开发的。

ziplist 是由一系列特殊编码的内存块构成的列表(像内存连续的数组,但每个元素长度不同), 一个 ziplist 可以包含多个节点(entry)。

ziplist 将表中每一项存放在前后连续的地址空间内,每一项因占用的空间不同,而采用变长编码。

当元素个数较少时,Redis 用 ziplist 来存储数据,当元素个数超过某个值时,链表键中会把 ziplist 转化为 linkedlist,字典键中会把 ziplist 转化为 hashtable。

由于内存是连续分配的,所以遍历速度很快。

在3.2之后,ziplist被quicklist替代。但是仍然是zset底层实现之一。

ziplist内存布局

ziplist使用连续的内存块,每一个节点(entry)都是连续存储的;ziplist 存储分布如下:

常态的压缩列表内存布局如上图所示,整个内存块区域内分为五个部分,下面分别介绍着五个部分:

zlbytes:存储一个无符号整数,固定四个字节长度,用于存储压缩列表所占用的字节,当重新分配内存的时候使用,不需要遍历整个列表来计算内存大小。

zltail:存储一个无符号整数,固定四个字节长度,代表指向列表尾部的偏移量,偏移量是指压缩列表的起始位置到指定列表节点的起始位置的距离。

zllen:压缩列表包含的节点个数,固定两个字节长度,源码中指出当节点个数大于2^16-2个数的时候,该值将无效,此时需要遍历列表来计算列表节点的个数。

entries:列表节点区域,长度不定,由列表节点紧挨着组成。每个节点可以保存一个字节数组或者是一个整数值。

zlend:一字节长度固定值为255,用于表示列表结束。

上面介绍了压缩列表的总体内存布局,对于初entries区域以外的四个区域的长度都是固定的,下面再看看此区域中每个节点的布局情况。

每个列表节点由三部分组成:

3623e2c0d76a8832b1ee7f5b51523c40.png

previous length:记录前一个节点所占有的内存字节数,通过该值,我们可以从当前节点计算前一个节点的地址,可以用来实现从表尾向表头节点遍历;

len/encoding:记录了当前节点content占有的内存字节数及其存储类型,用来解析content用;

content:保存了当前节点的值。

最关键的是prevrawlen和len/encoding,content只是实际存储数值的比特位。

为了节省内存,根据上一个节点的长度prevlength 可以将entry节点分为两类:

format,png

entry的前8位小于254,则这8位就表示上一个节点的长度

entry的前8位等于254,则意味着上一个节点的长度无法用8位表示,后面32位才是真实的prevlength。用254 不用255(11111111)作为分界是因为255是zlend的值,它用于判断ziplist是否到达尾部。

根据当前节点存储的数据类型及长度,可以将ziplist节点分为9类:

其中整数节点分为6类:

format,png

整数节点的encoding的长度为8位,其中高2位用来区分整数节点和字符串节点(高2位为11时是整数节点),低6位用来区分整数节点的类型,定义如下:

#define ZIP_INT_16B (0xc0 | 0<<4)//整数data,占16位(2字节)

#define ZIP_INT_32B (0xc0 | 1<<4)//整数data,占32位(4字节)

#define ZIP_INT_64B (0xc0 | 2<<4)//整数data,占64位(8字节)

#define ZIP_INT_24B (0xc0 | 3<<4)//整数data,占24位(3字节)

#define ZIP_INT_8B 0xfe //整数data,占8位(1字节)

/*4 bit integer immediate encoding*/

//整数值1~13的节点没有data,encoding的低四位用来表示data

#define ZIP_INT_IMM_MASK 0x0f

#define ZIP_INT_IMM_MIN 0xf1 /* 11110001 */

#define ZIP_INT_IMM_MAX 0xfd /* 11111101 */

值得注意的是 最后一种encoding是存储整数0~12的节点的encoding,它没有额外的data部分,encoding的高4位表示这个类型,低4位就是它的data。这种类型的节点的encoding大小介于ZIP_INT_24B与ZIP_INT_8B之间(1~13),但是为了表示整数0,取出低四位xxxx之后会将其-1作为实际的data值(0~12)。在函数zipLoadInteger中,我们可以看到这种类型节点的取值方法:

当data小于63字节时(2^6),节点存为上图的第一种类型,高2位为00,低6位表示data的长度。

当data小于16383字节时(2^14),节点存为上图的第二种类型,高2位为01,后续14位表示data的长度。

当data小于4294967296字节时(2^32),节点存为上图的第二种类型,高2位为10,下一字节起连续32位表示data的长度。

字符串节点分为3类:

format,png

当data小于63字节时(2^6),节点存为上图的第一种类型,高2位为00,低6位表示data的长度。

当data小于16383字节时(2^14),节点存为上图的第二种类型,高2位为01,后续14位表示data的长度。

当data小于4294967296字节时(2^32),节点存为上图的第二种类型,高2位为10,下一字节起连续32位表示data的长度。

上图可以看出:不同于整数节点encoding永远是8位,字符串节点的encoding可以有8位、16位、40位三种长度

相同encoding类型的整数节点 data长度是固定的,但是相同encoding类型的字符串节点,data长度取决于encoding后半部分的值。

#define ZIP_STR_06B (0 << 6)//字符串data,最多有2^6字节(encoding后半部分的length有6位,length决定data有多少字节)

#define ZIP_STR_14B (1 << 6)//字符串data,最多有2^14字节

#define ZIP_STR_32B (2 << 6)//字符串data,最多有2^32字节

如何通过一个节点向前跳转到另一个节点?

从尾部向头部遍历(利用 ztail 和privious_entry_length),用指向当前节点的指针 e , 减去前一个 entry的长度, 得出的结果就是指向前一个节点的地址 p 。

已知节点的位置,求data的值

entry布局 可以看出,若要算出data的偏移量,得先计算出prevlength所占内存大小(1字节和5字节):

//根据ptr指向的entry,返回这个entry的prevlensize

#define ZIP_DECODE_PREVLENSIZE(ptr, prevlensize) do { \

if ((ptr)[0]

(prevlensize)= 1; \

}else{ \

(prevlensize)= 5; \

} \

}while(0);

接着再用ZIP_DECODE_LENGTH(ptr + prevlensize, encoding, lensize, len)算出encoding所占的字节,返回给lensize;data所占的字节返回给len

//根据ptr指向的entry求出该entry的len(encoding里存的 data所占字节)和lensize(encoding所占的字节)

#define ZIP_DECODE_LENGTH(ptr, encoding, lensize, len) do { \ZIP_ENTRY_ENCODING((ptr), (encoding)); \if ((encoding)

(lensize)= 1; \

(len)= (ptr)[0] & 0x3f; \

}else if ((encoding) ==ZIP_STR_14B) { \

(lensize)= 2; \

(len)= (((ptr)[0] & 0x3f) << 8) | (ptr)[1]; \

}else if (encoding ==ZIP_STR_32B) { \

(lensize)= 5; \

(len)= ((ptr)[1] << 24) |\

((ptr)[2] << 16) |\

((ptr)[3] << 8) |\

((ptr)[4]); \

}else{ \

assert(NULL); \

} \

}else{ \

(lensize)= 1; \

(len)=zipIntSize(encoding); \

} \

}while(0);//将ptr的encoding解析成1个字节:00000000、01000000、10000000(字符串类型)和11??????(整数类型)//如果是整数类型,encoding直接照抄ptr的;如果是字符串类型,encoding被截断成一个字节并清零后6位

#define ZIP_ENTRY_ENCODING(ptr, encoding) do { \(encoding)= (ptr[0]); \if ((encoding) < ZIP_STR_MASK) (encoding) &=ZIP_STR_MASK; \

}while(0)//根据encoding返回数据(整数)所占字节数

unsigned int zipIntSize(unsigned charencoding) {switch(encoding) {case ZIP_INT_8B: return 1;case ZIP_INT_16B: return 2;case ZIP_INT_24B: return 3;case ZIP_INT_32B: return 4;case ZIP_INT_64B: return 8;default: return 0; /*4 bit immediate*/}

assert(NULL);return 0;

}

完成以上步骤之后,即可算出data的位置:ptr+prevlensize+lensize,以及data的长度len

连锁更新

每个节点的previous_entry_length属性都记录了前一个节点的长度

如果前一个节点的长度小于254,那么previous_entry_length属性需要用1字节长的空间来保存这个长度值

如果前一个节点的长度大于等于254,那么previous_entry_length属性需要5字节长的空间来保存这个长度值

考虑这样一种情况:在一个压缩列表中,有多个连续的、长度介于250字节到253字节之间的节点e1至eN

|zlbytes|zltail|zllen|e1|e2|e3|...|eN|zlend|

因为e1至eN的所有节点的长度都小于254字节,所以记录这些节点的长度只需要1字节长的previous_entry_length属性,换句话说,e1至eN的所有节点的previous_entry_length属性都是1字节长的。

如果我们将一个长度大于等于254字节的新节点new设置到压缩列表的表头节点,那么new将成为e1的潜质节点。

此时e1到eN的每个节点的previous_entry_length属性都要扩展为5字节以符合压缩列表对节点的要求,程序需要不断的对压缩列表进行空间重分配操作。

Redis将这种在特殊情况下产生的多次空间扩展操作称之为“连锁更新”。

除了添加新节点可能会引发连锁更新之外,删除节点也可能会连锁更新。

因为连锁更新在最坏情况下需要对压缩列表执行N次空间重分配操作,而每次空间重分配的最坏复杂度为O(N),所以连锁更新的最坏复杂度为O(N2)。

注意的是,尽管连锁更新的复杂度较高,但它真正赵成性能问题的几率是很低的:

首先,压缩列表里要恰好有多个连续的、长度介于250字节至253字节之间的节点,连锁更新才有可能被引发,在实际中,这种情况并不多见;

其次,即使出现连锁更新,但只要更新的节点数量不多,就不会对性能造成任何影响:比如说,对三五个节点进行连锁更新是绝对不会影响性能的;

Redis中压缩列表的应用

Redis中,不同的数据类型广泛地应用了压缩列表编码,整理如下表:

558eb3d49c22b21bb0e0fa6534db21a4.png

ziplist总结

ziplist的主要优点是节省内存,且ziplist存储在一段连续的内存上,所以存储效率很高。但是,它不利于修改操作,插入和删除操作需要频繁的申请和释放内存。

查找操作只能按顺序查找(可以是从前往后、也可以从后往前)

一旦数据发生改动,就会引发内存realloc,可能导致内存拷贝。当ziplist长度很长的时候,一次realloc可能会导致大批量的数据拷贝。

quickList

可以认为quickList,是ziplist和linkedlist二者的结合;quickList将二者的优点结合起来。

官方给出的定义:

A generic doubly linked quicklist implementation

A doubly linked list of ziplists

quickList是一个ziplist组成的双向链表。每个节点使用ziplist来保存数据。

本质上来说,quicklist里面保存着一个一个小的ziplist。结构如下:

format,png

/*quicklistNode is a 32 byte struct describing a ziplist for a quicklist.

* We use bit fields keep the quicklistNode at 32 bytes.

* count: 16 bits, max 65536 (max zl bytes is 65k, so max count actually < 32k).

* encoding: 2 bits, RAW=1, LZF=2.

* container: 2 bits, NONE=1, ZIPLIST=2.

* recompress: 1 bit, bool, true if node is temporarry decompressed for usage.

* attempted_compress: 1 bit, boolean, used for verifying during testing.

* extra: 12 bits, free for future use; pads out the remainder of 32 bits*/typedefstructquicklistNode {struct quicklistNode *prev; //上一个node节点

struct quicklistNode *next; //下一个node

unsigned char *zl; //保存的数据 压缩前ziplist 压缩后压缩的数据

unsigned int sz; /*ziplist size in bytes*/unsignedint count : 16; /*count of items in ziplist*/unsignedint encoding : 2; /*RAW==1 or LZF==2*/unsignedint container : 2; /*NONE==1 or ZIPLIST==2*/unsignedint recompress : 1; /*was this node previous compressed?*/unsignedint attempted_compress : 1; /*node can't compress; too small*/unsignedint extra : 10; /*more bits to steal for future usage*/} quicklistNode;/*quicklistLZF is a 4+N byte struct holding 'sz' followed by 'compressed'.

* 'sz' is byte length of 'compressed' field.

* 'compressed' is LZF data with total (compressed) length 'sz'

* NOTE: uncompressed length is stored in quicklistNode->sz.

* When quicklistNode->zl is compressed, node->zl points to a quicklistLZF*/typedefstructquicklistLZF {

unsignedint sz; /*LZF size in bytes*/

charcompressed[];

} quicklistLZF;/*quicklist is a 32 byte struct (on 64-bit systems) describing a quicklist.

* 'count' is the number of total entries.

* 'len' is the number of quicklist nodes.

* 'compress' is: -1 if compression disabled, otherwise it's the number

* of quicklistNodes to leave uncompressed at ends of quicklist.

* 'fill' is the user-requested (or default) fill factor.*/typedefstructquicklist {

quicklistNode*head; //头结点

quicklistNode *tail; //尾节点

unsigned long count; /*total count of all entries in all ziplists*/unsignedint len; /*number of quicklistNodes*/

int fill : 16; /*fill factor for individual nodes*///负数代表级别,正数代表个数

unsigned int compress : 16; /*depth of end nodes not to compress;0=off*///压缩级别

} quicklist;

quickList就是一个标准的双向链表的配置,有head 有tail;

每一个节点是一个quicklistNode,包含prev和next指针。

每一个quicklistNode 包含 一个ziplist,*zp 压缩链表里存储键值。

所以quicklist是对ziplist进行一次封装,使用小块的ziplist来既保证了少使用内存,也保证了性能。

refer:

《Redis设计与实现》

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/485846.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

神经符号系统、因果推理、跨学科交互,李飞飞、Judea Pearl等16名学者共同探讨AI未来...

来源&#xff1a;机器之心去年&#xff0c;纽约大学心理学和神经科学教授 Gary Marcus 和深度学习先驱、2018 年图灵奖得主 Yoshua Bengio 就 AI 技术的发展方向展开了一场现场辩论。今年&#xff0c;Gary Marcus 与 Montreal.AI 负责人 Vincent Boucher 举办了第二场辩论。这次…

数字孪生城市应用【案例集】,附下载

来源&#xff1a;中国信通院等数字孪生城市经过三年的概念培育期&#xff0c;目前已经步入落地实施阶段&#xff0c;各地不同程度地推进数字孪生城市规划建设和行业应用创新实践。中国信息通信研究院联合数字孪生城市生态圈合作伙伴&#xff0c;在推出《数字孪生城市白皮书&…

安乐高机器人图纸_乐高机器人拼装图纸 | 手游网游页游攻略大全

发布时间&#xff1a;2015-12-26这是一个非常酷的乐高机器人,可以绘画的机器人.意大利工程师Daniele Benedettelli制造了这个乐高机器人,称其为"LEGONARDO".他的绘画动作是通过编程实现的,他将不会停止,直到你 ...标签&#xff1a;游戏资讯 八卦杂谈发布时间&#xf…

重磅!中国微纳机器人技术取得新突破,实现肿瘤的可视化精准治疗!

来源&#xff1a;机器人大讲堂科技的发展会带动相关领域共同进步&#xff0c;在科技进步越来越快的今天&#xff0c;一项技术的进步往往会同时打开好几个领域的技术难关&#xff0c;产生连锁反应&#xff0c;科技的边界也逐渐模糊&#xff0c;以新材料石墨烯的研究为例&#xf…

什么是本体论?

来源&#xff1a;人机与认知实验室【世界有无本体还是互为本体&#xff1f;这是两个非常有意思的问题&#xff0c;从牛顿绝对主义角度应该有&#xff0c;从爱翁相对主义应该无&#xff0c;从未来的发展看&#xff0c;应该是“变”——也许就是“易”&#xff1f;&#xff01;主…

亚马逊首席技术官预测2021年将改变世界的八大技术趋势

来源&#xff1a;世界科技研究与发展2020年12月17日&#xff0c;亚马逊全球副总裁、首席技术官维尔纳•沃格尔&#xff08;Werner Vogels&#xff09;博士在亚马逊re:Invent全球大会上发表压轴演讲&#xff0c;分享了他对2021年科技趋势的预测。回顾2020年&#xff0c;Werner表…

《城市大脑全球标准研究报告2020》摘要内容

‍‍前言&#xff1a;《城市大脑全球标准研究报告2020》12月23在京正式发布&#xff0c;有近70位专家、领导和媒体记者出席了发布会&#xff0c;会后包括新华社、工人日报、中新社、科技日报、中国科学报、中国社会科学报、中国建设报、经济日报、中国经营报、经济观察报&#…

EasyExcel中处理内容占多个单元格

在一些业务需求中内容需要占用多个单元格的情况&#xff0c;如下图&#xff1a; 或者是这样 这样 总有一些奇怪怪的需求。 不过使用EasyExcel可以轻松处理这些变态的需求。EasyExcel中提供了ContentLoopMerge 注解就是为了处理这种问题的。下面先看看如何使用ContentLoopMe…

2021十大科技趋势来了!阿里巴巴达摩院全新发布

来源&#xff1a; 阿里技术阿里巴巴达摩院发布2021十大科技趋势&#xff0c;这是达摩院成立三年以来第三次发布年度科技趋势。2020年是不平凡的一年&#xff0c;经历疫情的洗礼&#xff0c;许多行业重启向上而生的螺旋&#xff0c;但疫情并未阻挡科技前进的脚步&#xff0c;量子…

从GPT-3到DETR,一起来盘点2020有哪些突破?

来源&#xff1a;深度学习技术前沿2020年是巨大飞跃的一年。从OpenAI的GPT-3&#xff0c;再到AlphaFold&#xff0c;都是令人振奋的成就。与此同时&#xff0c;数据科学在机器学习、自然语言处理&#xff08;NLP&#xff09;、计算机视觉等领域中蓬勃发展。一起来逐一盘点2020的…

我国5G基站达71.8万个,助力人工智能发展!

来源&#xff1a;新华社2020中国人工智能高峰论坛暨中国人工智能大赛成果发布会23日在厦门举办。论坛上&#xff0c;中国工业和信息化部副部长刘烈宏介绍&#xff0c;截至今年11月&#xff0c;我国累计建成5G基站71.8万个&#xff0c;为人工智能海量数据的成长和传输提供了坚实…

《自然》预测2021年值得关注的科学事件

来源&#xff1a;世界科技研究与发展作者&#xff1a;黄小容2020年12月22日&#xff0c;Nature官网发布了对2021年最值得关注科学事件的预测。1 气候变化问题卷土重来

重磅!基金委发布科研不端行为调查处理办法

编辑 ∑Gemini来源&#xff1a;国家自然科学基金委关于印发《国家自然科学基金项目科研不端行为调查处理办法》的通知国科金发诚〔2020〕96号各局&#xff08;室&#xff09;、科学部&#xff0c;机关党委&#xff0c;各直属单位&#xff1a;《国家自然科学基金项目科研不端行为…

不能编程、烧钱、没用?潘建伟直播回应“九章”量子计算争议

来源&#xff1a;腾讯科技在200秒时间内&#xff0c;76个光子穿过中国科学技术大学潘建伟团队精心构筑的光学网络&#xff0c;完成了5000万个样本的高斯玻色采样。而同样一道数学题交给世界上最顶尖 的超级计算机“富岳”&#xff0c;需要6亿年&#xff0c;差距超过了百万亿&a…

2021年,神经科学AI有这几大趋势

来源&#xff1a;The Next Web作者&#xff1a;Tristan Greene编译&#xff1a;科技行者新的一年正向我们招手。延续优良的革命传统&#xff0c;又到了发布最新一期AI专家预测报告的时候。各位受访专家将结合自己的所感所知、实验室发现以及企业动态为我们预测新一年中人工智能…

深度遍历和广度遍历

深度优先 例如下图&#xff0c;其深度优先遍历顺序为 1->2->4->8->5->3->6->7 广度优先 如下图&#xff0c;其广度优先算法的遍历顺序为&#xff1a;1->2->3->4->5->6->7->8 转载于:https://www.cnblogs.com/bigman-bugman/p/920252…

java ejb项目_Maven创建EJB项目结构

可以用maven创建EJB项目的结构。1、打开cmd2、输入一下内容mvn archetype:generate -DarchetypeGroupIdorg.codehaus.mojo.archetypes -DarchetypeArtifactIdpom-root -DarchetypeVersion1.1 -DarchetypeRepositoryhttp://repo.maven.apache.org/maven2 -DgroupIdcom.XXX -Dart…

2020年人工智能十大技术进展

pixabay.com来源&#xff1a;知识分子 撰文 &#xff1a; 全体智源学者制版编辑&#xff1a;卢卡斯编者按编者按2020年即将过去&#xff0c;今年人工智能领域有哪些重大进展&#xff1f;位于北京的智源人工智能研究院请 “智源学者” 们从全球的研究成果中评选了一份年度成就名…

CentOS 6快捷安装RabbitMQ教程

1.安装Erlang yum install erlang 2.安装RabbitMQ yum install rabbitmq-server 3.配置开机自启动 chkconfig rabbitmq-server on 4.启动RabbitMQ service rabbitmq-server start 5.查询RabbitMQ路径 whereis rabbitmq 6.进入目录 7.开启RabbitMQ的Web管理界面 ./rabbitmq-plug…

java视频压缩 lz4_关于LZMA和LZ4压缩的疑惑解析

原标题&#xff1a;关于LZMA和LZ4压缩的疑惑解析这是第112篇UWA技术知识分享的推送。今天我们继续为大家精选了若干和开发、优化相关的问题&#xff0c;建议阅读时间10分钟&#xff0c;认真读完必有收获。UWA QQ群&#xff1a;465082844(仅限技术交流)AssetBundleQ&#xff1a;…