在 Redis 中,哈希(Hash)数据类型在某些情况下会使用压缩列表(ziplist)来存储数据,而不是使用哈希表(hashtable)。使用压缩列表可以节省内存,并且在元素数量较少时,性能也是可以接受的。
使用压缩列表的条件
Redis 在以下条件下会使用压缩列表来存储哈希数据:
元素数量限制:
当哈希表中的字段数量(field-value pairs)小于或等于
hash-max-ziplist-entries
配置项的值时,会使用压缩列表。默认情况下,这个值通常是 512。
元素大小限制:
当哈希表中的每个字段和值的长度(字节数)小于或等于
hash-max-ziplist-value
配置项的值时,会使用压缩列表。默认情况下,这个值通常是 64 字节。
配置项
可以通过修改 Redis 配置文件(redis.conf
)或使用 CONFIG SET
命令来调整这些配置项:
hash-max-ziplist-entries
:控制哈希表中字段数量的最大值,超过这个值时会转换为哈希表。hash-max-ziplist-value
:控制哈希表中字段和值的最大长度,超过这个值时会转换为哈希表。
示例配置
在 redis.conf
文件中,可以找到如下配置项:
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
使用压缩列表的优点
节省内存:
压缩列表通过将多个元素紧凑地存储在一个连续的内存块中,减少了内存碎片和指针开销,从而节省内存。
性能适中:
对于小型哈希表,压缩列表的插入和删除操作的性能是可以接受的,尽管时间复杂度为 (O(n)),但在元素数量较少时,性能损失不大。
使用哈希表的场景
当哈希表中的字段数量或字段和值的长度超过上述配置项的限制时,Redis 会自动将压缩列表转换为哈希表。哈希表提供了 (O(1)) 的平均时间复杂度的查找、插入和删除操作,适合存储较大的哈希表。
压缩列表的内存结构
zlbytes:
4 字节无符号整数,表示整个压缩列表占用的内存字节数,包括
zlbytes
本身。
zltail:
4 字节无符号整数,表示压缩列表中最后一个元素的偏移量,即从压缩列表的起始位置到列表中最后一个元素的偏移量。这个字段用于快速定位最后一个元素,方便在列表尾部进行添加和删除操作。
zllen:
2 字节无符号整数,表示压缩列表中元素的数量。如果元素数量超过 65535,这个字段将不再准确,需要遍历整个列表来确定元素数量。
entryX:
压缩列表中的元素,每个元素由以下部分组成:
prevlen:前一个元素的长度,用于反向遍历。这个字段的长度可变,可以是 1 字节或 5 字节。
encoding:表示当前元素的编码方式,可以是字符串编码或整数编码。编码方式决定了
content
字段的存储格式。content:实际的元素内容,根据
encoding
字段的不同,可以是字符串或整数。
zlend:
1 字节特殊标记,值为 255(0xFF),表示压缩列表的结束。
压缩列表元素的编码
压缩列表中的每个元素可以根据其内容的长度和类型采用不同的编码方式:
字符串编码:
如果元素是字符串,
encoding
字段会指明字符串的长度,content
字段存储实际的字符串数据。
整数编码:
如果元素是整数,
encoding
字段会指明整数的类型(如 16 位整数、32 位整数等),content
字段存储实际的整数数据。
示例
假设有一个压缩列表,存储了三个元素:"foo"、"bar" 和 123。其内存结构可能如下:
zlbytes: 0x00000025 (37 字节)
zltail: 0x0000001C (28 字节)
zllen: 0x0003 (3 个元素)
entry1: prevlen: 0x00 (0 字节)
encoding: 0x0A (字符串编码,长度为 3)
content: "foo"
entry2: prevlen: 0x03 (3 字节)
encoding: 0x0A (字符串编码,长度为 3)
content: "bar"
entry3: prevlen: 0x03 (3 字节)
encoding: 0xC0 (整数编码,16 位整数)
content: 0x007B (123)
zlend: 0xFF
总结
压缩列表是一种紧凑的内存数据结构,通过将多个元素存储在一个连续的内存块中,节省内存空间。其内存结构包括头部信息、元素列表和尾部标记。每个元素由前一个元素的长度、编码方式和内容组成。压缩列表适用于存储小型列表和哈希表,能够高效地利用内存。Redis 中的哈希数据类型在满足一定条件时会使用压缩列表来存储数据,以节省内存。这些条件包括字段数量和字段、值的长度限制。通过合理配置这些参数,可以在内存使用和性能之间找到平衡。
评论区