Redis数据结构-链表和压缩列表

13 阅读2分钟

链表

Redis List类型底层的数据结构就是链表,Redis的链表是双向链表,每个节点都有prev和next指针,以及value存储元素

以下是RedisListNode的结构

public class RedisListNode{
    private RedisListNode prev;
    private RedisListNode next;
    private Object value;
}

Redis的List结构长这样

public class RedisList{
    private RedisList head;
    private RedisList tail;
    private int len;
}

链表作为List数据类型的优点

  • 每个节点指针是双向的,从任意一个节点都可以轻松找到前一个节点和后一个节点
  • 有len作为长度的计数,获取长度的时间复杂度是O(1)
  • 记录了head节点和tail节点,这样可以轻松的以O(1)的时间复杂度找到第一个节点和最后一个节点

缺点

  • 记录了前后指针,每个指针占用一定的内存
  • 链表的内存不是连续的,不能很好的利用cpu缓存

因此,Redis在List数据元素很少时,使用的是压缩列表

压缩列表

压缩列表的结构如下

[zlbytes][zltail][zllen][entry][entry][entry][zlend]
  • zlbytes:记录整个压缩列表占用的内存字节数:在对压缩列表进行内存重分配,或者计算 zlend 的位置时使用。
  • zltail: 记录压缩列表表尾节点距离压缩列表的起始地址有多少字节:通过这个偏移量,程序无需遍历整个压缩列表就可以确定表尾节点的地址。
  • zllen:记录了压缩列表包含的节点数量,当这个属性的值小于 UINT16_MAX(65535)时,这个属性的值就是压缩列表包含节点的数量;当这个值等于 UINT16_MAX 时,节点的真实数量需要遍历整个压缩列表才能计算得出。
  • zlend:特殊值 0xFF(十进制 255),用于标记压缩列表的末端。

entry的数据结构为:

image.png previous_entry_length:字段表示前一个元素的字节长度,占 1 个或者 5 个字节:

  • 当前一个元素的长度小于 254 字节时,用 1 个字节表示;
  • 当前一个元素的长度大于或等于 254 字节时,用 5 个字节来表示

所以当压缩列表插入一个元素时,下一个元素的previous_entry_length就需要改变,如果刚好前一个元素大于254字节,previous_entry_length就需要增加到5个字节,当前元素的字节又变了,如果刚好又大于254字节,那么下一个元素的previous_entry_length又要变,这就是ziplist在特殊情况下,会有连锁更新的问题;

ziplist的优点:

  • 内存连续,可以更好利用cpu的缓存
  • entry没有那么多指针,节省内存

缺点

  • 会有连锁更新问题