ziplist
压缩列表是列表键和哈希键的底层实现之一。
当一个列表键只包含少量表项,并且每个列表项要么是小整数,要么是较短的字符串 ,那么redis就会使用压缩列表来作为列表键的底层实现。
当一个哈希键只包含少量key-value对,且每个key-value对的key和value要么是小整数,要么是较短字符串,那么redis就会使用ziplist作为哈希键的底层实现。
ziplist的实现:
ziplist的内存布局如下所示:
- zlbytes:4字节,记录整个压缩列表占用内存的字节数
- zltail:4字节,记录压缩列表尾部节点距离起始地址的偏移量
- zllen:2字节,记录压缩列表包含的节点数量
- entry:不定,列表中的每个节点
- zlend:1字节,特殊值0xFF,标记压缩列表的结束
因此通过下面的宏定义可以非常方便的求出各个字段的值
#define ZIPLIST_BYTES(zl) (*((uint32_t*)(zl)))
#define ZIPLIST_TAIL_OFFSET(zl) (*((uint32_t*)((zl)+sizeof(uint32_t))))
#define ZIPLIST_LENGTH(zl) (*((uint16_t*)((zl)+sizeof(uint32_t)*2)))
#define ZIPLIST_HEADER_SIZE (sizeof(uint32_t)*2+sizeof(uint16_t))
#define ZIPLIST_ENTRY_HEAD(zl) ((zl)+ZIPLIST_HEADER_SIZE)
#define ZIPLIST_ENTRY_TAIL(zl) ((zl)+intrev32ifbe(ZIPLIST_TAIL_OFFSET(zl)))
#define ZIPLIST_ENTRY_END(zl) ((zl)+intrev32ifbe(ZIPLIST_BYTES(zl))-1)
一个简单的ziplist示意图如下:
zlbytes为0x50,表示压缩列表一共占用了0x50=80字节,其中zltail指示最后一个节点距离起始地址的偏移量为0x3C=60字节,因此p+60就为最后一个节点的起始地址,zllen=3表示列表中一共有3个节点。
ziplist节点定义如下:
typedef struct zlentry {
unsigned int prevrawlensize, prevrawlen;
unsigned int lensize, len;
unsigned int headersize;
unsigned char encoding;
unsigned char *p;
} zlentry;
- prevrawlen:前置节点的长度
- prevrawlensize:编码 prevrawlen 所需的字节大小
- len:当前节点的长度
- lensize:编码 len 所需的字节大小
- headersize:当前节点 header 的大小,等于 prevrawlensize + lensize
- encoding:当前节点值所使用的编码类型
- p:指向当前节点的指针
ziplist的创建
#define ZIPLIST_HEADER_SIZE (sizeof(uint32_t)*2+sizeof(uint16_t))