面试官:Redis压缩表是什么数据类型的底层实现

413 阅读4分钟

前言

本文将介绍 Redis List 和 Hash 的底层数据结构实现。

压缩列表

压缩列表(ziplist)是列表键和哈希键的底层实现之一,当一个列表键只包含少量列表项,并且每个列表项要么就是小整数值,要么就是长度比较短的字符串,那么Redis就会使用压缩列表来做列表键的底层实现。

例如,创建一个压缩列表实现的列表键:

redis> RPUSH lst 1 3 5 "hello" "world"
(integer) 6

redis> OBJECT ENCODING lst
"ziplist"

列表键中包含的都是小整数值以及短字符串。另外,当一个哈希键只包含少量键值对,且每个键值对的键和值要么就是小整数值,要么是长度短的字符串,那么Redis会使用压缩列表做哈希键的底层实现。

redis> HMSET profile "name" "Jack" "age" 28
OK

redis> OBJECT ENCODING profile
"ziplist"

哈希键中包含的所有键和值都是小整数值或短字符串。

定义

压缩列表是Redis为了节约内存而开发的,是由一系列特殊编码的连续内存块组成的顺序型(sequential)数据结构。一个压缩列表可以包含任意多个节点( entry ),每个节点可以保存一个字节数组或者一个整数值。

压缩表

压缩表的结构如下:

image.png

image.png

压缩表示例:

image.png

  • 列表 zlbytes 属性值为 0x50(十进制80),代表压缩列表总长为80字节
  • 列表 zltail 属性值为 0x3c(十进制60),代表如果我们有一个指向压缩列表起始地址的指针p,那么只要用指针p加上偏移量60,就可以计算出表尾节点 entry3 的地址。
  • zllen属性值为 0x3(十进制3),代表压缩列表包含三个节点

压缩表节点

每个压缩列表节点可以保存一个节点数组或者一个整数值,其中,字节数组可以是以下中的其中一种:

  • 长度小于等于63(2的6次幂-1)字节的字节数组;
  • 长度小于等于16383(2的14次幂-1)字节的字节数组;
  • 长度小于等于4294967295(2的32次幂-1)字节的字节数组;

而整数值则可以是以下六种长度的其中一种:

  • 4位长,介于0至12之间的无符号整数;
  • 1字节长的有符号整数;
  • 3字节长的有符号整数;
  • int16_t类型整数;
  • int32_t类型整数;
  • int64_t类型整数

每个压缩表节点由三个部分组成。

image.png

previous_entry_length

节点的previous_entry_length属性以字节为单位,记录了压缩列表中前一个节点的长度。previous_entry_length属性的长度可以是1字节或者5字节:

  • 如果前一节点的长度小于254字节,那么previous_entry_length属性的长度为1字节:前一节点的长度就保存在这一个字节里面
  • 如果前一节点的长度大于等于254字节,那么previous_entry_length属性的长度为5字节:其中属性的第一字节会被设置为0xFE(十进制值254),而之后的四个字节则用于保存前一节点的长度。

encoding

节点的 encoding 属性记录了节点的 content 属性所保存数据的类型以及长度:

  • 一字节、两字节或者五字节长,值的最高位为00、01或者10的是字节数组编码:这种编码表示节点的content属性保存着字节数组,数组的长度由编码除去最高两位之后的其他位记录;
  • 一字节长,值的最高位以11开头的是整数编码:这种编码表示节点的content属性保存着整数值,整数值的类型和长度由编码除去最高两位之后的其他位记录;

如下分别为字节数组编码和整数编码:

image.png

image.png

content

节点的 content 属性负责保存节点的值,节点值可以是一个字节数组或整数,值的类型和长度由节点的 encoding 属性决定。

下图是一个保存着字节数组"hello world" 的节点:

image.png

  • 编码最高两位00表示节点保存的是一个字节数组
  • 编码后六位001011记录了字节数组的长度11

下图是一个保存着整数10086 的节点:

image.png

  • 编码最高两位表示保存的是一个 int16_t 类型的整数值
  • content 属性保存着节点的值 10086

小结

本文讲述了Redis的list和hash数据结构的底层实现之一,当一个列表键只包含少量列表项,并且每个列表项要么就是小整数值,要么就是长度短的字符串,那么Redis会使用压缩表做list的底层实现。

如对 Redis感兴趣,可继续关注本专栏。