2024-05-02
版权声明:本文为博主付费文章,严禁任何形式的转载和摘抄,维权必究。 本文链接:https://www.skjava.com/mianshi/baodian/detail/2118730429

回答

Redis 中的 List 数据结构是一个双向链表,用于存储一个序列的数据,它类似于 Java 中的数组或列表,其底层实现分为两个版本:

  • 3.2 版本以前使用 linkedlist + ziplist
    • 当列表中元素的⻓度较⼩或者数量较少时,通常采⽤zipList来存储。原因是因为 zipList 是一个紧凑的数据结构,能够有效地减少内存占用。但是,在列表中元素较多或者元素较大时,zipList 的性能会下降,因为在列表的头部或尾部添加或删除元素时,可能需要重新分配并复制整个 ziplist。所以,zipList 非常适合少量的小数据存储。同时 zipList 还有一个“连锁更新”的问题,也会严重影响 ziplist 的性能。
    • 当列表元素大于 512 或者元素的长度大于 64 字节时,List 底层由 zipList 转换为linkedlistlinkedlist 是一个双向链表,能够快速支持列表的删除和插入操作,但是内存利用率较低,因为它需要额外的内存空间来维护链表结构。
  • 3.2 版本以后使用 quicklist
    • 从 3.2 版本开始后,List 的底层实现采用 quicklistquicklist 是将 zipList 作为节点嵌入 linkedList 的节点中,它结合了两者的优点,也具备两者的优点。具体来说,quicklist 是由多个 ziplist 节点组成的 linkedList 链表,每个 ziplist 节点可以存储多个列表元素。

扩展

List 介绍

List 的 Redis 中的 5 种主要数据结构之一,它是一种序列集合,可以存储一个有序的字符串列表,顺序是插入的顺序。我们可以使用相关命令添加一个字符串元素到 List 的头部(左边)或者尾部。

**List 的最大长度为 2^31 - 1,即每个 List 支持超过 40 亿个元素。**主要特点如下:

  • 有序性:List 中的元素按照插入顺序排序,可以在列表的头部或尾部添加元素。
  • 双向链表:List 内部通过双向链表实现,使得在列表的两端操作(如插入和删除)都非常快,时间复杂度为 O(1)。
  • 元素重复性:List 允许重复的元素。
  • 元素访问:List 支持通过索引访问元素(如 LINDEX 命令),但这是通过遍历实现的,因此其时间复杂度为 O(N)。若 List 元素较多,则访问效率较低

List 常用的命令如下:

  • LPUSH key value1 [value2]:将一个或多个元素插入到列表头部。如果 key 不存在,一个空列表会被创建并执行 LPUSH 操作。返回值是操作后列表的长度。
  • RPUSH key value1 [value2]:将一个或多个值插入到列表尾部。类似于 LPUSH
  • LPOP key:移除并返回列表的第一个元素。如果列表为空,返回 nil。
  • RPOP key:移除并返回列表的最后一个元素。如果列表为空,返回 nil。
  • LLEN key:返回列表的长度,如果 key 不存在 返回 0。
  • LINDEX key index:获取列表在 index 位置的元素。index 是基于 0 的,也可以是负数,-1 表示最后一个元素,-2 表示倒数第二个元素等。
  • LSET key index value:将列表的 index 位置的值设置为 value。如果 index 超出范围,操作会返回一个错误。
  • LRANGE key start stop:获取列表指定范围内的元素。start 和 stop 都是基于 0 的索引,可以使用负数索引指定位置。
  • LREM key count value:根据参数 count 的值,移除与参数 value 相等的元素。count 的值可以是以下几种:
    • count > 0:从头到尾,移除最多 count 个 value 相等的元素。
    • count < 0:从尾到头,移除最多 -count 个 value 相等的元素。
    • count = 0:移除所有 value 相等的元素。
  • LTRIM key start stop:对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除。

下面是 List 常用命令的演示:

需要注意的是,List 设置过期时间,只能给整个 List 设置过期时间,不能单独给某一个元素设置。

List 的底层实现

List 的底层实现有三种:zipListlinkedListquickList。他们的使用情况如下:

  • 当 List 存储的元素较少且每个元素的大小也较小时,Redis 会选择使用 zipList 来存储数据,以节省内存空间。
  • 当列表元素大于 512 或者元素的长度大于 64 字节时,Redis 则转换为使用 linkedList 来存储数据,以优化操作的性能。

zipList

当 List 中的元素比较少或者每个元素的大小也小时,Redis 选择 zipList 来存储数据。zipList 通过紧凑的内存布局存储一系列的 entry,每个 entry 可以代表一个字符串或者整数。

zipList 的内部结构主要分为三个部分:表头(header)、条目(entries)和表尾(end),如下图: