Redis - listpack(紧凑列表)图文详解

本文详细解析了Redis 7.0中listpack的出现,旨在优化ziplist的连锁更新问题。通过对比ziplist和listpack的内存结构,讨论了listpack如何通过去掉前一个节点长度记录来降低性能消耗,并介绍了新的操作接口和参数变化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一 前言

1.1 背景

在阅读本文前,需要了解下 ziplist(压缩列表),因为 listpack 的出现是用来代替 ziplist 的。

Redis 采用 ziplist ,是因为其为一种连续内存空间并且有序的压缩链表 。在数据节点不多的情况下,内存占用和查询复杂度得到一个相对较好的平衡。

但是 zip 有个一个致命的缺陷,就是极端情况下的连锁更新会带来不小的性能消耗。如下图所示:
在这里插入图片描述

1.2 方案

Redis 在后续的版本中也采用了 quicklist(快速链表),通过控制 quicklistNode 结构里的压缩列表的大小或者元素个数,来减少连锁更新带来的性能影响,但是并没有完全解决连锁更新的问题,因为 quicklistNode 还是用了压缩列表来保存元素

所以在 Redis5.0 出现了 listpack,目的是替代压缩列表,其最大特点是 listpack 中每个节点不再包含前一个节点的长度,压缩列表每个节点正因为需要保存前一个节点的长度字段,就会有连锁更新的隐患。

二 源码解读

鉴入 Redis7.0 已经将 listpack 完整替代 ziplist(Redis7.0 新特性) ,所以本文的源码是 7.0版本。

前文提到 listpack 最大特点是 listpack 中每个节点不再包含前一个节点的长度,所以直接对比 listpack 和 ziplist 的结构设计。

2.1 ziplist entry

typedef struct zlentry {
   
    unsigned int prevrawlensize; /* 用于编码前一个节点字节长度*/
    unsigned int prevrawlen;     
    unsigned int lensize;        /* 用于编码此节点类型/长度的字节。
    								例如,字符串有1、2或5个字节标题。
    								整数总是使用一个字节。
    							*/
    unsigned int len;            /* 用于表示节点实际的字节。
									对于字符串,这只是字符串长度
									而对于整数,它是1、2、3、4、8或
									0,具体取决于数字范围。 
								*/
    unsigned int headersize;     /* prevrawlensize + lensize. */
    unsigned char encoding;      /* 设置为ZIP_STR_*或ZIP_INT_*,具体取决于节点编码。*/
    unsigned char *p;            /* 第一个节点的地址指针,prev-entry-len */
} zlentry;

可以明显看到 prevrawlensize 用于记录前一个节点的大小。

2.2 listpack entry

typedef struct {
   
    /* 当使用string时,它具有长度(slen)。 */
    unsigned char *sval;
    uint32_t slen;
    /* 当使用integer时,“sval”为 NULL,lval 保存该值。*/
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

mooddance

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值