redis底层数据结构-quicklist

Redis的list数据类型底层由quicklist实现,quicklist结合了双向链表和ziplist的优点。ziplist是内存紧凑的有序列表,但不利于频繁修改;quicklist减少内存碎片,但节点ziplist长度需平衡。Redis通过`list-max-ziplist-size`和`list-compress-depth`配置参数来优化内存使用。

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

redis底层数据结构-quicklist
quicklist
  • Redis对外暴露的list数据类型,它底层实现所依赖的内部数据结构就是quicklist。
  • quicklist是一个双向链表,而且是一个ziplist的双向链表,也就是说quicklist的每个节点都是ziplist
ziplist
  • ziplist是一个有序列表,而且是一个内存紧缩的列表,即一整块连续内存(各个数据项在内存上前后相邻)
quicklist和ziplist 优缺点
  • 双向链表便于在表的两端进行push和pop操作,但是它的内存开销比较大,它在每个节点上除了要保存数据之外,还要额外保存两个指针;其次,双向链表的各个节点是单独的内存块,地址不连续,节点多了容易产生内存碎片。

  • ziplist由于是一整块连续内存,所以存储效率很高。但是,它不利于修改操作,每次数据变动都会引发一次内存的realloc。特别是当ziplist长度很长的时候,一次realloc可能会导致大批量的数据拷贝,进一步降低性能。

存在问题

到底一个quicklist节点包含多长的ziplist合适呢?比如,同样是存储12个数据项,既可以是一个quicklist包含3个节点,而每个节点的ziplist又包含4个数据项,也可以是一个quicklist包含6个节点,而每个节点的ziplist又包含2个数据项。

每个quicklist节点上的ziplist越短,则内存碎片越多。内存碎片多了,有可能在内存中产生很多无法被利用的小碎片,从而降低存储效率。这种情况的极端是每个quicklist节点上的ziplist只包含一个数据项,这就蜕化成一个普通的双向链表了。

每个quicklist节点上的ziplist越长,则为ziplist分配大块连续内存空间的难度就越大。有可能出现内存里有很多小块的空闲空间(它们加起来很多),但却找不到一块足够大的空闲空间分配给ziplist的情况。这同样会降低存储效率。这种情况的极端是整个quicklist只有一个节点,所有的数据项都分配在这仅有的一个节点的ziplist里面。这其实蜕化成一个ziplist了。

redis配置

一个quicklist节点上的ziplist要保持一个合理的长度。那到底多长合理呢?这可能取决于具体应用场景。实际上,Redis提供了一个配置参数list-max-ziplist-size,就是为了让使用者可以来根据自己的情况进行调整。

 list-max-ziplist-size -2

当取正值的时候,表示按照数据项个数来限定每个quicklist节点上的ziplist长度。比如,当这个参数配置成5的时候,表示每个quicklist节点的ziplist最多包含5个数据项。

当取负值的时候,表示按照占用字节数来限定每个quicklist节点上的ziplist长度。这时,它只能取-1到-5这五个值,每个值含义如下:

-5: 每个quicklist节点上的ziplist大小不能超过64 Kb。(注:1kb => 1024 bytes)

-4: 每个quicklist节点上的ziplist大小不能超过32 Kb。

-3: 每个quicklist节点上的ziplist大小不能超过16 Kb。

-2: 每个quicklist节点上的ziplist大小不能超过8 Kb。(-2是Redis给出的默认值)

-1: 每个quicklist节点上的ziplist大小不能超过4 Kb。
极致的节省空间

list的设计目标是能够用来存储很长的数据列表的, 当列表很长的时候,最容易被访问的很可能是两端的数据,中间的数据被访问的频率比较低(访问起来性能也很低)。如果应用场景符合这个特点,那么list还提供了一个选项,能够把中间的数据节点进行压缩,从而进一步节省内存空间。Redis的配置参数list-compress-depth就是用来完成这个设置的。

 list-compress-depth 0

0: 是个特殊值,表示都不压缩。这是Redis的默认值。

1: 表示quicklist两端各有1个节点不压缩,中间的节点压缩。

2: 表示quicklist两端各有2个节点不压缩,中间的节点压缩。

3: 表示quicklist两端各有3个节点不压缩,中间的节点压缩。

依此类推...
### Redis 列表的底层数据结构 Redis列表采用快速链表(quicklist)作为其底层数据结构[^2]。这种设计融合了双向链表与压缩列表(ziplist)的优点。 #### 快速链表 (QuickList) 快速链表本质上是一种优化后的双向链表,其中每个节点不仅指向前后相邻的节点,还包含了一个压缩列表 ziplist。这意味着每一个 quicklist 节点实际上封装了一个小型的 ziplist 结构,从而允许更高效的内存管理和访问模式[^3]。 ```c typedef struct quicklist { // ...其他成员... quicklistNode *head; quicklistNode *tail; } quicklist; typedef struct quicklistNode { unsigned char encoding; /* 编码方式 */ int count; /* 当前ziplist中的元素数量 */ int mem_usage; /* 使用了多少字节 */ quicklistEntry entry; /* 用于迭代器 */ struct quicklistNode *prev, *next;/* 前驱和后继指针*/ unsigned char *zl; /* 指向ziplist的实际数据 */ } quicklistNode; ``` #### ZIPLIST 特性 Ziplist 是一种紧凑型线性序列化容器,在单个连续分配的缓冲区内存储一系列项。它特别适合于短字符串或小整数值,并能显著减少内存碎片并提高缓存命中率。然而,随着长度增加,操作效率可能会受到影响;因此,当达到一定阈值时,系统会选择切换至更为传统但占用更多空间的实现方法[^1]。 #### API 和自动调整机制 为了简化开发者的工作流程,Redis 提供了一系列高级别的命令接口来处理列表的操作,比如 LPUSH、RPUSH 等等。这些API能够透明地管理内部使用的具体数据结构形式——无论是初始状态下的ziplist还是扩展之后形成的完整双向链表形态。此外,根据实际应用场景的需求变化,Redis 还可以在运行期间动态改变所选用的数据结构类型以保持最佳性能表现。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值