redislist底层数据结构 前面学习解了redis的简单字符串sds的结构。这次来学习下List的底层数据结构总概 Redis中list底层实现有三种linkedListzipListquickListlinkedList 与java中的linkedList类似。定义链表节点的结构体typedfstructlistNode{前一个节点structlistNodeprev;后一个节点structlistNodenext;当前节点的值的指针voidvalue;}listNode;链表结构typedfstructlist{头指针,即指向链表头部节点listNodehead;尾指针,指向尾部节点listNodetail;节点拷贝函数void(dup)(voidptr);释放节点函数void(free)(voidptr);判断两个节点是否相等的函数int(match)(voidptr,voidkey);链表长度unsignedlonglen;}zipListzipList的数据结构typedfstructziplistT{压缩列表占用字符数int32zlbytes;最后一个元素距离起始位置的偏移量,用于快速定位最后一个节点通过这个参数可以支持双向访问int32zltailoffset;元素个数int16zllength;元素内容T〔〕entries;结束位0xFFint8zlend;}ziplist;entrytypedestructentry{前一个entry的长度intvarprelen;元素类型编码intvarencoding;元素内容optionalbyte〔〕content;}entry; entry解析: prelen保存的是前一个entry节点的长度,这样在倒序遍历时就可以通过这个参数定位到上一个entry的位置。encoding保存了content的编码类型。content则是保存的元素内容,它是optional类型的,表示这个字段是可选的。当content是很小的整数时,它会内联到content字段的尾部。entry结构的示意图如下所示: zipList遍历时,先根据zlbytes和zltailoffset定位到最后一个entry的位置,然后再根据最后一个entry里的prelen时确定前一个entry的位置。zipList的连锁更新 entry中prelen字段,它的长度要么是1个字节,要么都是5个字节:前一个节点的长度小于254个字节,则prelen长度为1字节;前一个节点的长度大于254字节,则prelen长度为5字节; 假设现在有一组压缩列表,长度都在250253字节之间,突然新增一个entry节点,这个entry节点长度大于等于254字节。由于新的entry节点大于等于254字节,这个entry节点的prelen为5个字节,随后会导致其余的所有entry节点的prelen增大为5字节。 同样地,删除操作也会导致出现连锁更新这种情况,假设在某一时刻,插入一个长度大于等于254个字节的entry节点,同时删除其后面的一个长度小于254个字节的entry节点,由于小于254的entry节点的删除,大于等于254个字节的entry节点将会与后面小于254个字节的entry节点相连,此时就与新增一个长度大于等于254个字节的entry节点时的情况一样,将会发生连续更新。发生连续更新时,Redis需要不断地对压缩列表进行内存分配工作,直到结束。linkedList和zipList对比zipList相比于linkedList,其少了pre和next两个指针。在Redis中,pre和next指针就要占用16个字节(64位系统的一个指针就是8个字节)。另外,linkedList的每个节点的内存都是单独分配,加剧内存的碎片化,影响内存的管理效率。与之相对的是,zipList是由连续的内存组成的,这样一来,由于内存是连续的,就减少了许多内存碎片和指针的内存占用,进而节约了内存。linkedList便于在表的两端进行push和pop操作,在插入节点上复杂度很低,但是它的内存开销比较大。zipList存储在一块连续的内存上,所以存储效率很高。但是它不利于修改操作,插入和删除操作需要频繁地申请和释放内存。 因此当列表对象中元素的长度较小或者数量较少时,通常采用zipList来存储;当列表中元素的长度较大或者数量比较多的时候,则会转而使用双向链表linkedList来存储。quickList Redis3。2版本之后,list的底层实现方式又多了一种,quickList。qucikList是由zipList和双向链表linkedList组成的混合体。它将linkedList按段切分,每一段使用zipList来紧凑存储,多个zipList之间使用双向指针串接起来。示意图如下所示: typedfstructquicklistNode{前一个节点quicklistNodeprev;后一个节点quicklistNodenext;压缩列表ziplistzl;ziplist大小int32size;ziplist中元素数量int16count;编码形式存储ziplist还是进行LZF压缩储存的zipListint2encoding;。。。}quickListNode;typedfstructquicklist{指向头结点quicklistNodehead;指向尾节点quicklistNodetail;元素总数longcount;quicklistNode节点的个数intnodes;压缩算法深度intcompressDepth;。。。}quickList;quckList中zipList大小 打开redis。conf文件了。在DVANCEDCONFIG下面有着清晰的记载。Listsarealsoencodedinaspecialwaytosavealotofspace。Thenumberofentriesallowedperinternallistnodecanbespecifiedasafixedmaximumsizeoramaximumnumberofelements。Forafixedmaximumsize,use5through1,meaning:5:maxsize:64Kbnotrecommendedfornormalworkloads4:maxsize:32Kbnotrecommended3:maxsize:16Kbprobablynotrecommended2:maxsize:8Kbgood1:maxsize:4KbgoodPositivenumbersmeanstoreuptoexactlythatnumberofelementsperlistnode。Thehighestperformingoptionisusually2(8Kbsize)or1(4Kbsize),butifyourusecaseisunique,adjustthesettingsasnecessary。listmaxziplistsize2 quickList内部默认单个zipList长度为8k字节,即listmaxziplistsize的值设置为2,超出了这个阈值,就会重新生成一个zipList来存储数据。根据注释可知,性能最好的时候就是就是listmaxziplistsize为1和2,即分别是4kb和8kb的时候,当然,这个值也可以被设置为正数,当listmaxziplistszie为正数n时,表示每个quickList节点上的zipList最多包含n个数据项。压缩深度 quickList中可以使用压缩算法对zipList进行进一步的压缩,这个算法就是LZF算法,这是一种无损压缩算法,具体可以参考这里。使用压缩算法对zipList进行压缩后,zipList的结构如下所示:typedfstructziplistcompressed{元素个数int32size;元素内容byte〔〕compresseddata} 在redis。conf文件中的DVANCEDCONFIG下面也可以对压缩深度进行配置。Listsmayalsobecompressed。Compressdepthisthenumberofquicklistziplistnodesfromeachsideofthelisttoexcludefromcompression。Theheadandtailofthelistarealwaysuncompressedforfastpushpopoperations。Settingsare:0:disablealllistcompression1:depth1meansdontstartcompressinguntilafter1nodeintothelist,goingfromeithertheheadortailSo:〔head〕nodenode。。。node〔tail〕〔head〕,〔tail〕willalwaysbeuncompressed;innernodeswillcompress。2:〔head〕〔next〕nodenode。。。node〔prev〕〔tail〕2heremeans:dontcompressheadorheadnextortailprevortail,butcompressallnodesbetweenthem。3:〔head〕〔next〕〔next〕nodenode。。。node〔prev〕〔prev〕〔tail〕etc。listcompressdepth0 listcompressdepth这个参数表示一个quickList两端不被压缩的节点个数。需要注意的是,这里的节点个数是指quicklist双向链表的节点个数,而不是指ziplist里面的数据项个数。实际上,一个quicklist节点上的ziplist,如果被压缩,就是整体被压缩的。quickList默认的压缩深度为0,也就是不开启压缩当listcompressdepth为1,表示quickList的两端各有1个节点不进行压缩,中间结点进行压缩;当listcompressdepth为2,表示quickList的首尾2个节点不进行压缩,中间结点进行压缩;以此类推小结 封面图侵权删