游戏电视苹果数码历史美丽
投稿投诉
美丽时装
彩妆资讯
历史明星
乐活安卓
数码常识
驾车健康
苹果问答
网络发型
电视车载
室内电影
游戏科学
音乐整形

redis源码阅读ziplist

  ziplist1。介绍
  首先是官方介绍:
  Theziplistisaspeciallyencodedduallylinkedlistthatisdesignedtobeverymemoryefficient。Itstoresbothstringsandintegervalues,whereintegersareencodedasactualintegersinsteadofaseriesofcharacters。ItallowspushandpopoperationsoneithersideofthelistinO(1)time。However,becauseeveryoperationrequiresareallocationofthememoryusedbytheziplist,theactualcomplexityisrelatedtotheamountofmemoryusedbytheziplist。
  ziplist是一个经过特殊编码的双向链表,旨在提高内存效率。它可以储存字符串和整型值,其中,整型值被编码为实际整数而不是作为字符储存。ziplist可以在O(1)的时间内,在其左侧和右侧进行pop和push操作。但是,实际每个操作都需要重新分配ziplist使用的内存,因此实际的复杂性与ziplist使用的内存量有关。1。1。内存分布
  每个ziplist占用的内存布局如下:
  其中,zlbytes是一个无符号整数,表示当前ziplist占用的总字节数。zltail是ziplist最后一个entry的指针相对于ziplist最开始的偏移量。通过它,不需要完全遍历ziplist就可以对最后的entry进行操作。zllen是ziplist的entry数量。当zllen比2162大时,需要完全遍历entry列表来获取entry的总数目。zlend是一个单字节的特殊值,等于255,标识着ziplist的内存结束点。1。2。entry
  ziplist每个entry都有一个header,header中有两部分内容。第一部分内容是前一个entry的字节数prevrawlen。第二部分内容是该entry的编码encoding,如果是字符串编码,会附带储存其长度len。每一个entry中的结构如下,content为entry实际储存的数据,包括整型和字符串。
  第一部分中,上一个entry的长度prevrawlen使用如下方式编码:如果其长度小于254字节,entry使用一个字节储存如果其长度大于等于254字节,当前entry使用5个字节。第一个字节设置为254,表示后面的四个字节储存的是上一个entry的长度
  第二部分信息编码形式encoding取决于当前entry数据类型:如果需要储存的数据是字符串,这部分信息的前两位设置为对应的编码,后续内容储存字符串的长度如果需要储存的数据是整数,这部分信息的前两位设置为11,之后的两位用来确定整数类型。例如:00pppppp1byte
  长度小于等于63字节的string,后6位储存其长度
  01ppppppqqqqqqqq2bytes
  长度小于等于16383字节的string,后14位储存其长度
  10qqqqqqqqrrrrrrrrsssssssstttttttt5bytes
  长度大于等于16384字节的string,后32位储存其长度
  110000001byte
  Integerencodedasint16t(2bytes)。
  110100001byte
  Integerencodedasint32t(4bytes)。
  111000001byte
  Integerencodedasint64t(8bytes)。
  111100001byte
  Integerencodedas24bitsigned(3bytes)。
  111111101byte
  Integerencodedas8bitsigned(1byte)。
  1111xxxx(withxxxxbetween0000and1101)immediate4bitinteger。
  xxxx这四位中,0000、1111、1110都不能使用,因此其编码后的值范围为113。
  11111111Endofziplist。2。函数和宏实现2。1。宏定义
  redis就是通过一堆宏定义来加速程序执行的。2。1。1。ZIPISSTR(enc)
  通过entry中的编码enc来判断当前entry中持有的数据类型是不是stringdefineZIPISSTR(enc)(((enc)ZIPSTRMASK)ZIPSTRMASK)
  ZIPSTRMASK是0b00110000,将它与enc进行AND操作,如果enc是string类型,那么其前两位应该是00、01或10,因此计算之后的数值应该小于ZIPSTRMASK的。2。1。2。ZIPLISTBYTES(zl)
  这个宏就是把ziplist最开头的zlbytes提取出来,代码也很简单,通过指针取zl对应的值。defineZIPLISTBYTES(zl)(((uint32t)(zl)))2。1。3。ZIPLISTTAILOFFSET(zl)
  这个宏是提取ziplist的zltail内容,它的指针则是对zl偏移一个uint32t大小(zlbytes的长度)获得的。defineZIPLISTTAILOFFSET(zl)(((uint32t)((zl)sizeof(uint32t))))2。1。4。ZIPLISTLENGTH(zl)
  获取zllen的内容,其指针地获取同上,只不过需要偏移两个uint32t的内存大小。defineZIPLISTLENGTH(zl)(((uint16t)((zl)sizeof(uint32t)2)))2。1。5。ZIPLISTENTRY相关
  redis使用三个宏来定位ziplist中entry的首尾位置。
  首先计算了ziplist中第一个entry到ziplist开头的偏移地址,其实就是zlbytes、zltail和zllen占用的内存大小。defineZIPLISTHEADERSIZE(sizeof(uint32t)2sizeof(uint16t))
  接下来通过宏获得ziplist第一个entry的指针地址。defineZIPLISTENTRYHEAD(zl)((zl)ZIPLISTHEADERSIZE)
  那么ziplist的最后一个entry的指针地址也可以通过2。1。3的宏获得。defineZIPLISTENTRYTAIL(zl)((zl)intrev32ifbe(ZIPLISTTAILOFFSET(zl)))
  entry列表的地址边界也可以获得,ziplist最后是一个字节的zlend,因此,zl偏移zlbytes1就可以获得其指针了。defineZIPLISTENTRYEND(zl)((zl)intrev32ifbe(ZIPLISTBYTES(zl))1)2。1。6。ZIPLISTINCRLENGTH(zl,incr)
  这个宏用来调整ziplist的entry数量,即zllen。因为ziplist每次只pop或push一个数据,因此这个宏的incr一般为1或1。
  代码如下,当ZIPLISTLENGTH(zl)大于UINT16MAX时,就已经不再更新zllen了,之后统计ziplist长度就需要进行遍历。defineZIPLISTINCRLENGTH(zl,incr){if(ZIPLISTLENGTH(zl)UINT16MAX)ZIPLISTLENGTH(zl)intrev16ifbe(intrev16ifbe(ZIPLISTLENGTH(zl))incr);}
  我当时迷糊了好久,为啥第三行代码ZIPLISTLENGTH(zl)的结果可以直接赋值呢?我翻了好久也没翻出来,突然发现,ZIPLISTLENGTH(zl)是宏定义。它在编译时是将定义的代码直接插入到第三行代码中的,这样就可以进行指针赋值了。2。1。7。ZIPENTRYENCODING(ptr,encoding)
  这个宏是用来设置entry的encoding字段,具体entry的结构见后续章节。
  代码如下:defineZIPENTRYENCODING(ptr,encoding)do{(encoding)(ptr〔0〕);if((encoding)ZIPSTRMASK)(encoding)ZIPSTRMASK;}while(0)
  代码中第三行,如果encoding小于ZIPSTRMASK(0b11000000),就通过AND操作将encoding后6位设置为0。2。1。8。ZIPDECODELENGTH(ptr,encoding,lensize,len)
  这个宏的实现目标为:根据encoding设置lensize,即len占用的字节数;根据ptr指向的数据设置len。
  接下来介绍lensize和len的设置,具体编码参考1。2章的encoding格式:如果encodingZIPSTR06B(0b00000000),它的lensize为1,len即为ptr〔0〕的后六位。如果encodingZIPSTR14B(0b01000000),lensize为2,len为((ptr〔0〕0x3f)8)ptr〔1〕。如果encodingZIPSTR32B(0b11000000),lensize为5,len为ptr〔1〕ptr〔4〕组成的uint64类型整数。否则的话encoding0b11000000,说明其是数字,则调用zipIntSize(encoding)设置它的len,lensize为1。staticunsignedintzipIntSize(unsignedcharencoding){switch(encoding){caseZIPINT8B:return1;caseZIPINT16B:return2;caseZIPINT24B:return3;caseZIPINT32B:return4;caseZIPINT64B:return8;default:return0;4bitimmediate}assert(NULL);return0;}2。1。9。ZIPDECODEPREVLENSIZE(ptr,prevlensize)和ZIPDECODEPREVLEN(ptr,prevlensize,prevlen)
  在1。2章节中,entry会记录上一个entry的大小,如果超过254则使用5个字节来储存其长度,否则只使用1个字节就够了。因此,ZIPDECODEPREVLENSIZE(ptr,prevlensize)就是用来设置entry中的prevlensize字段。defineZIPDECODEPREVLENSIZE(ptr,prevlensize)do{if((ptr)〔0〕ZIPBIGLEN){(prevlensize)1;}else{(prevlensize)5;}}while(0);
  那ZIPDECODEPREVLEN(ptr,prevlensize,prevlen)就很简单了,就是把ptr〔1〕ptr〔4〕的数据复制到prevlen。defineZIPDECODEPREVLEN(ptr,prevlensize,prevlen)do{ZIPDECODEPREVLENSIZE(ptr,prevlensize);if((prevlensize)1){(prevlen)(ptr)〔0〕;}elseif((prevlensize)5){assert(sizeof((prevlensize))4);memcpy((prevlen),((char)(ptr))1,4);memrev32ifbe(prevlen);}}while(0);2。2。函数
  ziplist的函数实现,从ziplist的创建、插入等操作开始叙述。2。2。1。charziplistNew(void)
  顾名思义,这个函数就是产生一个新的ziplist。
  代码:unsignedcharziplistNew(void){unsignedintbytesZIPLISTHEADERSIZE1;unsignedcharzlzmalloc(bytes);ZIPLISTBYTES(zl)intrev32ifbe(bytes);ZIPLISTTAILOFFSET(zl)intrev32ifbe(ZIPLISTHEADERSIZE);ZIPLISTLENGTH(zl)0;zl〔bytes1〕ZIPEND;returnzl;}
  见1。1章节ziplist的内存结构,ziplist初始时没有entry,因此其结构中只存在zlbytes、zltail、zllen和zlend四个,其中,zlbytes和zltail分别占用4个字节,zllen占用2个字节,zlend占用一个字节。因此代码中的bytes计算得11个字节。
  分配完对应的内存后,使用上一章的宏定义设置ziplist的相关参数。
  最后,把zl〔bytes1〕即最后一个字节设置为0xff,即ZIPEND。2。2。2。zlentryzipEntry(unsignedcharp)
  这个函数用来生成一个新的entry,entry的结构如下:typedefstructzlentry{prevrawlen表示上一个entry的大小、prevrawlensize表示prevrawlen占用的字节数unsignedintprevrawlensize,prevrawlen;len表示当前entry的长度lensize表示len占用的字节数unsignedintlensize,len;headersize表示entry的头部占用字节数headersizeprevrawlensizelensizeunsignedintheadersize;encoding表示当前entry使用的编码unsignedcharencoding;p指向的数据是当前entry的实际内容,包括prevrawlen、encoding和contentunsignedcharp;}zlentry;
  因此,zipEntry的实现也很简单,entry的prevrawlen和len都储存在p指向的地址中,使用2。1。9的相关宏进行设置。
  上面的entry结构是便于同前端交互而实现的结构,实际ziplist中的entry中只有prevrawlen、lenencoding以及数据content组成。2。2。3。数据插入
  ziplist支持从头部和尾部进行数据的插入,这两种方法都需要使用ziplistInsert实现。函数签名如下:unsignedcharziplistInsert(unsignedcharzl,unsignedcharp,unsignedchars,unsignedintslen)
  函数中zl就是实际的ziplist实体,p为需要插入的entry指针位置,s为需要插入的字符串,长度为slen。
  实现逻辑如下:计算prevlensize和prevlen,如果p在zl的最后位置,则通过ptail进行计算,否则直接通过p就可以计算得到。if(p〔0〕!ZIPEND){如果p不是zl的最后位置,新数据是插在p后面的,因此通过p可以获得prevlensize和prevlenZIPDECODEPREVLEN(p,prevlensize,prevlen);}else{p是zl的最后位置新数据插入在ptail后面那么需要获取ptail的长度给prevlenunsignedcharptailZIPLISTENTRYTAIL(zl);if(ptail〔0〕!ZIPEND){prevlenzipRawEntryLength(ptail);}}使用zipTryEncoding(s,slen,value,encoding)判断能否将s字符串转换为数字,并设置相应的encoding;如果无法转换,ziplist将使用字符串编码方式进行储存。使用ziplistResize(zl,curlenreqlennextdiff)增加zl的内存,并进行内存移动操作,留出可以放置新entry的空间。新entry下一跳entry的prevlen需要进行调整,在1。2章节中,prevrawlen可能为1或5个字节。在本节中,如果新entry的长度len一个字节就够编码,而下一跳entry的prerawlen之前使用5个字节编码,那么就需要进行内存的调整。具体见ziplistCascadeUpdate(zl,preqlen)的代码实现。根据需要插入的数据类型(string或整数)添加到ziplist中,并增加zllen。2。2。4。查找
  函数签名如下,p为需要查找的entry列表起点,vstr为需要查找的数据,长度为vlen,每次查找时跳过skip个entry进行匹配。例如,skip2,第一次比较entry0,那么entry1、entry2都会跳过不进行比较,下一次直接比较entry3。unsignedcharziplistFind(unsignedcharp,unsignedcharvstr,unsignedintvlen,unsignedintskip)
  这个函数的实现思路为,从第一个entry依次遍历到最后一个entry,即p〔0〕ZIPEND。通过宏定义获取p指向entry的prevlensize、lensize和len。qpprevlensizelensize,得到当前entry的数据所在地址。根据encoding,进行分别比较。如果为string,则使用memcmp比较,否则使用zipTryEncoding将vstr转为整数进行比较。在循环中,使用skipcnt进行跳过。将pqlen,指向下一个entry。2。2。5。遍历
  在对ziplist进行遍历时需要获取下一个entry的地址,遍历分前向和后向遍历,因此,ziplist也有两种实现方式。unsignedcharziplistNext(unsignedcharzl,unsignedcharp)unsignedcharziplistPrev(unsignedcharzl,unsignedcharp)
  在ziplistNext中,如果p已经遍历到zl的最后位置ZIPEND,下一个entry就是NULL;否则的话使用zipRawEntryLength获取p的entry占用字节数,然后将p向后移动相应的字节。如果移动后p指向ZIPEND,则返回NULL。
  在ziplistPrev中,如果p指向ZIPEND,那么p的上一个entry就是zltail指向的entry,将其返回即可;如果p是zl的第一个entry,那么返回NULL;否则的话使用ZIPDECODEPREVLEN计算当前entry中的prevlen,将pprevlen并返回。2。2。6。删除
  函数签名如下,删除从p开始的num个entry,并返回删除后的ziplist。unsignedcharziplistDelete(unsignedcharzl,unsignedcharp,unsignedintnum)
  实现思路如下:计算需要删除的entry占用的内存字节数totlen。如果totlen为0,直接返回zl如果totlen0,说明需要进行内存的移动。删除entry时,第num1个entry的prevrawlen就需要重新调整,可能从1字节变为5字节,因此需要使用zipPrevLenByteDiff函数判断需要调整的字节数nextdiff,然后p向前移动nextdiff个字节数。重新设置zlbytes、zllen、zltail等参数,返回zl2。2。7。entry数量
  获取zl中entry的数量就比较简单了。如果zllen小于UINT16MAX,直接就可以返回zllen;否则的话需要从第一个entry遍历到最后一个entry进行统计。代码就不贴了,可以看源代码中的ziplistLen(unsignedcharzl)函数。总结
  ziplist当数据量比较大的时候,其查找还是很消耗资源的,需要进行完全遍历,时间复杂度最坏情况为O(n);它的插入和删除的时间复杂度也并不一致是O(1)。因此在使用时也需要配合其他数据结构使用。
  刚开始接触ziplist的时候不知道它的具体原理是什么,但是只要了解ziplist的内存分布以及entry的内存分布,它的相关函数实现逻辑基本就了然于胸了,不过具体的细节还是需要阅读源码来提高理解的。

空降利刃乔梁乔栋是什么关系乔梁和乔栋是一个人吗观看电视剧《空降利刃》的人都能够发现,剧中乔梁和乔栋两人长的太像了,不少人觉得他们俩是双胞胎,乔梁和乔栋到底是什么关系呢?乔梁和乔栋是一个人吗?一起来看一下。空降利刃乔梁……豆瓣高分电影电视剧有哪些(30部高分悬疑)豆瓣高分电影电视剧有哪些?其实,说到悬疑一般都比较烧脑的,本文综合30部高分悬疑剧悬疑片分享给大家,感兴趣的去了解了解。第1部:《窃听风暴》《窃听风暴》剧情是这样讲……老年人的福音,低速电动车老头乐即将转正,工信部正式发文低速电动车,也就是老年人俗称的老头乐,一直是中国汽车市场奇特存在,它既不属于乘用车,也没有专门适用的法规,安全隐患很大,但是它的需求量巨大,保有量也同样很大,是一个拥有百万辆规……有一个按摩的电影关于按摩的电影盘点有一个按摩的电影你看过吗?关于按摩的电影:泰惊魂之无骨按摩师、山的那边,德市之恋、快乐按摩女郎、按摩师与女人、可疑的美容院、欲望爱人、索命暹罗之按摩师、一路向西、神秘的按摩师、……全新零跑T03上市,配置动力均提升,微型电动车开始内卷了?在造车新势力中,大部分的目光都落在了一线阵营蔚小理身上,事实上,其他造车新势力也在默默蓄力,零跑汽车就是其中之一。12月28日,2022款零跑T03正式上市,新车共推出5……鹤唳华亭太子妃是怎么死的太子妃到底有没有流产电视剧《鹤唳华亭》正在热播当中,看过这部剧的观众无一不觉得很是虐心,其中被称为白月光的太子妃竟然也下线了,太子妃是怎么死的?太子妃到底有没有流产,为什么会流产?一起来看一下具体……亚马逊航空公司在美国新开价值15亿美元的货运枢纽亚马逊航空公司(AmazonAir)在辛辛那提北肯塔基国际机场(CVG)新开设了价值15亿美元的大型航空枢纽,将作为这家电子商务巨头在美国中部的货运枢纽。该中心花了四年时……2022年人工智能领域发展七大趋势2022年人工智能领域发展七大趋势有望在网络安全和智能驾驶等领域大显身手人工智能已成为人类有史以来最具革命性的技术之一。人工智能是我们作为人类正在研究的最重要的技术……骗子龙芯为你喝彩中兴被罚、华为被禁,缺芯之痛刺痛国人的心。2021初缺芯寒潮又席卷了全球,从全球知名车企大众、丰田、福特等因芯片供不应求而减产、关厂,再到手机、面板等消费电子也面临缺芯困局,芯……七月与安生结局啥意思,七月与安生结局谁死了情感剧《七月与安生》给带来性格迥异的安生和七月在高中时期成为闺蜜,她们一起经历校园和职场,还有亲情的多种考验后,得以成长起来和冰释前嫌,所相互扶持发展,最后七月产生难产死,而安……步步惊心丽结局是悲剧吗,步步惊心丽结局女主角和谁在一起了穿越剧《步步惊心丽》给带来在日全食现象中灵魂穿越到高丽的21世纪女人解树跟四王子王昭之间产生的浪漫爱情,还有高丽宫廷火热的王权竞争,直到最后给展现一个悲剧,而女主角选择跟十四在……9。8分以上的国产电视剧(2022整理),每一部都是良心剧一部影视作品的好坏虽然无法由评分决定,但是它的评分却实实在在地体现了大众的接受程度,在2022年有哪些9。8分以上的国产电视剧呢,一起来了解一下吧。19。8分以上的国产电……
AppStore限免时光倒数地铁跑酷2steps等,共5款在今天限免的iOS限免应用中,波老师精选了以下2款限免应用,3款限免游戏。复制logo上方名字即可前往AppStore下载。如遇恢复原价,则表示限免已结束,请谨慎下载。具……最新版攻略!澳门通关前,你得先知道这几件事最近小宝在后台收到很多关于澳门通关的咨询作为人美心善的小编,当然是有求必应那么废话不多说,针对大家问得最多、最关心的问题,小宝已整理出最新版……高空航拍塘朗山深圳湾塘朗梅林水库南山和龙华风光高空航拍塘朗山、深圳湾、塘朗、梅林水库、南山和龙华风光,由大疆御2专业版拍摄。塘朗山,属于鸡公山系,是位于深圳东北部的山峰。主峰塘朗峰海拔430米,面积约15000亩。登……SpringBoot并没有那么复杂,这一篇文章带你快速入门(SpringBoot是一个全新的框架,是用来简化Spring应用的初始搭建及开发过程的,可以使用特定的方式来进行配置,可使得开发人员不在需要定义样板化的配置,所以,Spring……首辆全新一代东风日产奇骏亮相深圳【新车新闻】2021年5月7日,全球第一大会展中心深圳国际会展中心(福永)车展拉开序幕,东风日产携NISSAN全车系阵容亮相,展具规模达500成为全馆最靓的仔,为深圳地区广大消……华为P40系列变身超级望远镜再创光学变焦新奇迹2020必备的5G手机,华为P40Pro配置超感光潜望式长焦摄像头拉近你和世界的距离。一个以128分刷新DxO的5G旗舰手机,有谁能够不心动呢?更何况还有五种神仙配色供你选择。……决策参考美团被罚款34亿余元联想集团科创板IPO审核终止1、市场监管总局处罚美团垄断行为:罚款34亿余元事件:2021年4月,市场监管总局依据《反垄断法》对美团在中国境内网络餐饮外卖平台服务市场滥用市场支配地位行为立案调查。经……萨尔瓦多比特币信托基金现已增值400万美元首只比特币ETF上市两周突破23亿美元根据ProShare公司的官网消息,截止2021年10月29日,由该公司发起并管理的全美首只比特币挂钩ETF、比特币策略ETF(代码……华为千元全面屏新机谍照疑似曝光重要部分却未露出最近,有网友曝光了几张华为最新款千元手机畅享7S的谍照。从图片上来看,这款新机采用了当下最流行的全面屏,整体机身格外精致纤薄,超大的屏占比迎合了主流审美。有专业人士称,这……英媒评出Top10中型轿车,日系无一入选,第一名仍是它英国著名汽车媒体《AUTOCAR》日前评选了2020年中型轿车Top10榜单。在榜单内我们看到了不少熟悉的车型,在国内销售中,最意外的是,居然没有日系的影子,看来英国媒体的评选……9。28成都璟睿智能携新品亮相2021未来全宅巡回论坛(成都9月28日,2021未来全宅巡回论坛参展商璟睿智能亮相2021未来全宅论坛(成都站)。9月28日,2021未来全宅巡回论坛(成都站)将于成都总府皇冠假日酒店盛大举行。继3……惊喜!二十多年后再读围城时隔20多年漫长岁月重新打开一本书,如同打开另一个完全不一样的感悟世界。最近有幸重新阅读了钱钟书老先生的《围城》,让我久久不能平静,因为它是一部以看似超然调侃语调述说人生无奈的……
友情链接:易事利快生活快传网聚热点七猫云快好知快百科中准网快好找文好找中准网快软网