Redis(四)Redis中的List

深入解析 Redis List 数据类型,从底层实现原理(ziplist、quicklist)到常见命令,全面理解列表的应用场景。

📝 List类型定义

List 列表是简单的字符串列表,按照插入顺序排序,可以从头部或尾部向 List 列表添加元素。

列表的最大长度为 $2^{32} - 1$,也即每个列表支持超过 40 亿个元素。

List 的特点与 LinkedList 一致:

  • 有序:元素按插入顺序排列
  • 元素可以重复
  • 插入和删除速度快
  • 查询速度一般

📋 常见命令

🔍 获取元素

  • LINDEX KEY_NAME INDEX_POSITION:通过索引获取列表中的元素。你也可以使用负数下标,以 -1 表示列表的最后一个元素,-2 表示列表的倒数第二个元素

➕ 插入元素

  • LPUSH KEY_NAME VALUE1.. VALUEN:将一个或多个值插入到列表头部。如果 key 不存在,一个空列表会被创建并执行 LPUSH 操作。当 key 存在但不是列表类型时,返回一个错误
  • LPUSHX KEY_NAME VALUE1.. VALUEN:将一个值插入到已存在的列表头部,列表不存在时操作无效
  • LINSERT key BEFORE|AFTER pivot value:将值 value 插入到列表 key 当中,位于值 pivot 之前或之后
  • RPUSH KEY_NAME VALUE1..VALUEN:将一个或多个值插入到列表的尾部(最右边)
  • RPUSHX KEY_NAME VALUE1..VALUEN:将一个值插入到已存在的列表尾部(最右边)。如果列表不存在,操作无效
  • BRPOPLPUSH LIST1 ANOTHER_LIST TIMEOUT:Redis Brpoplpush 命令从列表中取出最后一个元素,并插入到另外一个列表的头部。如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止

➖ 删除元素

  • BLPOP LIST1 LIST2 .. LISTN TIMEOUT:移出并获取列表的第一个元素。如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。如果列表为空,返回一个 nil;否则,返回一个含有两个元素的列表,第一个元素是被弹出元素所属的 key,第二个元素是被弹出元素的值
  • BRPOP LIST1 LIST2 .. LISTN TIMEOUT:移出并获取列表的最后一个元素。如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。假如在指定时间内没有任何元素被弹出,则返回一个 nil 和等待时长;反之,返回一个含有两个元素的列表,第一个元素是被弹出元素所属的 key,第二个元素是被弹出元素的值
  • LPOP KEY_NAME:移除并返回列表的第一个元素
  • RPOP KEY_NAME:移除并返回列表的最后一个元素

🧱 底层实现原理

List 类型的底层数据结构是由 双向链表或压缩列表 实现的:

  • 如果列表的元素个数小于 512 个(默认值,可由 list-max-ziplist-entries 配置),列表每个元素的值都小于 64 字节(默认值,可由 list-max-ziplist-value 配置),Redis 会使用 压缩列表(ziplist) 作为 List 类型的底层数据结构
  • 如果列表的元素不满足上面的条件,Redis 会使用 双向链表 作为 List 类型的底层数据结构

在 Redis 3.2 版本之后,List 数据类型底层数据结构就只由 quicklist 实现了,替代了双向链表和压缩列表。

提示

Ziplist(压缩列表):压缩列表是 由连续内存块组成的顺序型数据结构,有点类似于数组。

它的结构分为表头和节点两部分。表头有三个字段:zlbytes记录整个压缩列表的内存字节数,zltail记录尾节点的偏移量,zllen记录节点数量。末尾还有个zlend标记结束,固定值0xFF。因为有zltail,定位首尾元素的复杂度是 $O(1)$ ,但查找中间元素只能逐个遍历,复杂度是O(N),所以压缩列表不适合保存太多元素。

每个节点包含三部分:prevlen记录前一个节点的长度,用来支持从后向前遍历;encoding记录当前数据的类型和长度;data是实际数据。prevlen和encoding会根据数据大小采用不同的空间分配,这就是Redis节省内存的设计思想。

压缩列表示意图
  • 优点:简单、紧凑、连续存储,适用于小数据量场景
  • 缺点:压缩列表新增某个元素或修改某个元素时,如果空间不够,压缩列表占用的内存空间就需要重新分配。而当新插入的元素较大时,可能会导致后续元素的占用空间都发生变化,从而引起连锁更新问题,导致每个元素的空间都要重新分配,造成访问压缩列表性能的下降

Quicklist

  • 通过将链表和 Ziplist 结合,既实现了链表的灵活操作,又能节省内存
  • Quicklist 是为了替代纯链表而设计的,适用于需要频繁对列表进行插入、删除、查找等操作的场景,并且数据量可能较大。它在存储多个元素时,既保留了链表的灵活性,又具备压缩列表的内存优势
  • Quicklist 由 list 和 ziplist 结合而成,它是一个由 ziplist 充当节点的双向链表

🎯 应用场景

💡 简单消息队列

  • 消息保序性:使用 LPUSH + RPOP
消息队列示意图
  • 生产者使用 LPUSH key value[value...] 将消息插入到队列的头部,如果 key 不存在则会创建一个空的队列再插入消息
  • 消费者使用 RPOP key 依次读取队列的消息,先进先出
  • 消息重复性:自行为每个消息生成一个全局唯一 ID
  • 消息可靠性:让消费者程序从一个 List 中读取消息,同时,Redis 会把这个消息再插入到另一个 List(可以叫作备份 List)留存
最后更新于 2026-05-09 09:22 UTC
그 경기 끝나고 좀 멍하기 있었는데 여러분 이제 살면서 여러가
使用 Hugo 构建
主题 StackJimmy 设计