为了节约内存,在zset
和hash
容器对象元素个数较少时,Redis
会采用压缩列表(ziplist)
进行存储。
压缩列表是一块连续的内存空间,元素之间紧挨着存储,不存在冗余
一个压缩列表可以包含任意多个节点(entry)
,每个节点可以保存一个字节数组或者一个整数值
结构
// 压缩列表 struct ziplist<T> { int32 zlbytes; // 压缩列表占用的内存字节数 int32 zltail_offset; // 记录表尾节点距离起始地址有多少个字节,用于快速定位最后一个元素 int16 zllength; // 压缩列表包含的节点数 T[] entries; // 压缩列表包含的所有节点 int8 zlend; // 特殊值0xFF,标记压缩列表结尾 } ziplist;
// 列表节点 struct entry { int<val> prevlen; // 前一个entry节点的长度 int<val> encoding; // 节点的content属性保存的数据的类型 optional byte[] content; // 节点的值 } entry;
prevlen
字段长度是1
个字节或5
个字节:
- 前一个节点长度小于
254
:使用1
个字节 - 前一个节点长度大于等于
254
:使用5
个字节
增加元素
由于ziplist
是紧凑存储的,没有冗余空间,所以每一次插入新的元素都需要调用realloc
扩展内存。取决于内存分配算法和当前ziplist
内存大小,realloc
可能重新分配内存空间然后进行拷贝,也可能直接在原地址上进行扩展,不进行拷贝。
如果ziplist
占据内存太大,realloc
重新分配内存和拷贝会产生很大的消耗,所以ziplist
不适合存储大型字符串,存储元素也不宜过多。
级联更新
由于每一个entry
都有一个prevlen
属性,该属性可能是1
个字节或5
个字节,取决于前一个元素的长度,所以在前一个元素长度变更,即长度由大于等于254
变为小于254
或由小于254
变为大于等于254
时,会导致后一个节点的prevlen
属性更新。
如果后一个节点长度是253
,则该节点的后续节点也需要更新,依此类推,可能导致后续所有的节点都需要进行更新,这种在特殊情况下产生的连续多次空间扩展操作称之为级联更新。
级联更新在最坏情况下需要对ziplist
执行N
次内存重分配操作,而每次分配的最坏复杂度为O(N)
,所以级联更新的最坏复杂度为O(N^2)
。
尽管级联更新的复杂度较高,但是该操作造成性能问题的几率很低:
- 需要
ziplist
中恰好有多个连续的、长度介于250~253
个字节的节点才可能引发连续更新,该情况很少见 - 即使出现级联更新,只要被更新的节点数量不多,就不会对性能产生影响
原文地址:https://www.cnblogs.com/jeemzz/p/11442570.html
时间: 2024-11-05 20:40:44