前言
本文将介绍 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 ),每个节点可以保存一个字节数组或者一个整数值。
压缩表
压缩表的结构如下:
压缩表示例:
- 列表
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类型整数
每个压缩表节点由三个部分组成。
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属性保存着整数值,整数值的类型和长度由编码除去最高两位之后的其他位记录;
如下分别为字节数组编码和整数编码:
content
节点的 content
属性负责保存节点的值,节点值可以是一个字节数组或整数,值的类型和长度由节点的 encoding
属性决定。
下图是一个保存着字节数组"hello world" 的节点:
- 编码最高两位00表示节点保存的是一个字节数组
- 编码后六位001011记录了字节数组的长度11
下图是一个保存着整数10086 的节点:
- 编码最高两位表示保存的是一个 int16_t 类型的整数值
- content 属性保存着节点的值 10086
小结
本文讲述了Redis的list和hash数据结构的底层实现之一,当一个列表键只包含少量列表项,并且每个列表项要么就是小整数值,要么就是长度短的字符串,那么Redis会使用压缩表做list的底层实现。
如对 Redis
感兴趣,可继续关注本专栏。