打卡深入~压缩表和listpack|青训营笔记

241 阅读2分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的第五篇笔记

压缩表和listpack:

前面提到了很多次的压缩表和listpack,也稍微聊到了压缩表的优劣点,现在来具体看看它到底是什么东西

先说说最开始的压缩表:

压缩列表最大的特点就是地址连续,它跟数组一样占用一块连续空间,可以很好的利用到cpu缓存,而不同的是压缩列表每个结点的存储空间是按实际需求来的

压缩列表的表头定义了三个字段:zlbytes(列表占用字节数)、zltail(列表尾的偏移量)、zllen(结点数量)、表尾也定义了字段zlend标识结束点

通过这些字段,我们可以在O(1)内访问了头或尾,而每个结点除非了存储对应数据外,还存了自身的长度,上个结点的长度**(prevlen)**,所以可以快速的前往上或下个结点。

而prevlen虽然是一个int,但却是大小不固定的,当prevlen小于254时,我们知道,只要8位就可以存储,所以redis只使用了一个字节进行存储。而超过了254的,使用5个字节存储,如果数据是字符串的话,还有可能是2个字节。

这是redis减小内存开销的手段之一,看上去非常的nice,但是隐藏了一个问题。

压缩列表的使用者是list、hash、zset,但要求是每个节点都小于64字节,那其实pervlen只需要一个字节是够了,没必要用2或者5了。而查阅部分资料后发现,其实这个64是可以配置的。

那么就引出了另一个问题,连锁更新!

试想这样一个场景,我有一个压缩表,节点大小全部是254,然后我在中间插入一个大小是300的节点。

插入之后发生什么?下一个节点要更新prevlen,但是一个字节已经不够了,需要将prevlen变到5个字节,然后自身长度超过254,下个节点也要更新prevlen......

redis在5.0版本时用lispack取代了压缩表

listpack跟压缩表一样,同样使用了连续的内存空间进行存储,在表头尾的字段上,与压缩表相比,少了列表尾的偏移量,也就是无法在O(1)定义到表尾结点,因为listpack的节点不再存储上个节点的大小了,所以尾结点的O(1)访问就没什么作用了。

listpack节点中,除了存实际数据外,还存了编码格式和自身节点的大小

没错,listpack存了自身节点大小而不存其它节点相关数据,以此避免了连锁更新的问题。