压缩列表是Redis为节约内存而开发的,是由一系列特殊编码的连续内存块组成的顺序型数据结构。一个压缩列表可以包含任意多个节点,每个节点可以保存一个字节数组或者整数值。
压缩列表构成
- zlbytes: 记录整个压缩列表占用的内存字节数,对压缩列表进行内存重分配,或者计算zlend位置时使用
- zltail:记录压缩列表表尾节点距离压缩列表起始地址由多少字节,通过这个偏移量,程序无需遍历整个压缩列表就可以确定表尾节点的地址
- zllen:记录压缩列表包含的节点数量
- entryx:代表对应的节点
- zlend:表尾末端标识
压缩节点构成
previous_entry_length
previous_entry_length顾名思义存储的是前置节点的长度,previous_entry_length可以是1个字节或者5个字节。
- 如果前一个节点长度小于254字节,previous_entry_length属性长度为1字节,前一节点长度就保存在这个字节里面
- 如果前一个节点长度大于等于254字节,previous_entry_length属性长度为5字节,会进行标识设置和保存前一个节点的长度
节点的previous_entry_length属性记录了前一个节点的长度,所以可以通过指针运算,根据当前节点的起始地址计算出前一个节点的起始地址。压缩列表从表尾向表头的遍历操作就是使用的这一原理实现的,是要我们拥有了一个指向某个节点起始地址的指针,那么就可以通过这个指针及节点保存的previous_entry_length查询到前一个节点。依次回溯,就可以完成列表的逆向遍历。
encoding
encoding记录了节点content的保存数据的类型及长度
content
content负责保存节点的值,节点的类型和长度由encoding属性决定。
连锁更新
redis如果发生连续多次内存空间扩展成为连锁更新。
什么是连锁更新
每个节点的previous_entry_length保存着前一个节点的长度信息,如果前置节点发生变更时,previous_entry_length也会有可能变更。如果previous_entry_length长度发生变更,那么该节点的内存占用空间也会变更。如果本节点长度的变更,正好是后置节点的长度也在临界点(本节点长度更新后,节点长度大于254,且后置节点previous_entry_length发生变更,占用空间也由小于254变成了大于了254)发生空间占用变更。又再次引起其后置节点长度发生变化。
什么时候会发生连锁更新
如果某个节点的前置节点发生变更时,就可能发生长度的更新,所以压缩列表插入节点或者删除节点都要有可能引发连锁更新。
不过虽然引发连锁反应的代价很高,但是发生连锁反应的概率很低,他需要满足一下几个条件
- 首先,压缩列表里恰好有多个连续的,长度介于250-253字节之间的节点,连锁更新才有可能被引发
- 其次,即使出现连锁更新,如果节点数量不多,也不会对性能造成致命的影响。