Redis List 列表类型详解:从入门到实践


1. 为什么需要 List?

在 Redis 的五大基本类型中,List(列表) 是用来存储有序的、可重复的 字符串集合。它和 Java 中的 LinkedList 或 Python 中的 list 类似,但 Redis 的 List 是经过特殊设计的,能在头部和尾部进行高效的操作。

典型场景

  • 消息队列(生产者/消费者)

  • 最新消息列表(比如微博时间线)

  • 栈(后进先出)或队列(先进先出)

List 的有序性体现在每个元素都有一个下标(索引),从 0 开始计数(也可以负数表示倒数第几个),你可以通过索引访问任意位置的元素。


2. List 的核心概念

  • 元素有序:插入顺序决定了元素的存储顺序。

  • 元素可重复:同一个值可以出现多次。

  • 双向操作:从左边(头部)或右边(尾部)插入/弹出元素都非常快(O(1))。

  • 支持索引访问 :通过 LINDEXLRANGE 获取指定范围的元素。

一个 List 最多可以容纳 2^32 - 1(约 42 亿)个元素,绝大多数场景够用了。


3. 常用命令详解

我们从最基础的「增删改查」开始,一步步深入。

3.1 插入元素

LPUSH -- 从左侧插入(头插)
复制代码
LPUSH key element [element ...]
  • 将一个或多个元素从左边插入列表(可以理解为"头插")。

  • 返回插入后列表的长度。

  • 时间复杂度 O(N),N 为插入元素个数(但对于单个元素是 O(1))。

所以多次 LPUSH 后,列表是"后来的在前"。

RPUSH -- 从右侧插入(尾插)
复制代码
RPUSH key element [element ...]
  • 右边插入(尾插),同样返回新长度。
LPUSHX / RPUSHX -- 仅当 key 存在时插入

这两个命令与 LPUSH / RPUSH 类似,但只在 key 已存在时执行,如果 key 不存在,则什么也不做

3.2 弹出元素

LPOP -- 从左侧弹出(头删)
复制代码
LPOP key
  • 移除并返回列表最左边的元素(头元素)。

  • 如果列表为空,返回 nil

RPOP -- 从右侧弹出(尾删)
复制代码
RPOP -- 从右侧弹出(尾删)
  • 移除并返回最右边的元素。

LPOPRPOP 都是 O(1) 操作,非常高效。

3.3 获取范围/索引

LRANGE -- 获取指定范围的元素
复制代码
LRANGE key start stop
  • 返回从 startstop 的所有元素(闭区间,包含两端)。

  • start / stop 支持负数索引:-1 代表最后一个元素,-2 代表倒数第二个,以此类推。

  • 如果 start 大于实际长度,返回空列表;如果 stop 超出范围,只返回存在的部分。

注意:LRANGE 的时间复杂度是 O(N),N 是返回的元素个数。对于大列表,不建议取太大的范围。

LINDEX -- 获取指定索引位置的元素
复制代码
LINDEX key index
  • 返回索引 index 处的元素,支持负数(-1 为最后一个)

  • 如果索引超出范围,返回 nil

3.4 获取列表长度

LLEN
复制代码
LLEN key
  • 返回列表的长度(元素个数)。O(1) 操作。

3.5 删除元素

LREM -- 移除指定个数的特定元素
复制代码
LREM key count element
  • 从列表中移除count 值为 element 的元素。

  • count > 0:从左向右移除最多 count 个。

  • count < 0:从右向左移除最多 |count| 个。

  • count = 0:移除所有值为 element 的元素。

  • 返回实际移除的个数。

注意:LREM 的时间复杂度为 O(N),N 是列表长度,因为它需要遍历。

3.6 插入到指定元素前后

LINSERT -- 在某个元素前/后插入
复制代码
LINSERT key BEFORE|AFTER pivot element
  • 在列表中,在第一个值为 pivot 的元素之前之后 插入 element

  • 如果 pivot 不存在,则返回 -1 且不做插入。

  • 返回插入后列表的长度。

3.7 设置指定索引的值

LSET -- 修改指定位置的元素
复制代码
LSET key index value
  • 将索引 index 处的元素设置为 value

  • 如果索引超出范围,返回错误。

  • O(N) 操作(N 为列表长度,因为底层可能是链表)。

3.8 修剪列表

LTRIM -- 只保留指定范围的元素
复制代码
LTRIM key start stop
  • 保留 [start, stop] 范围内的元素,其余全部删除。

  • 可以用于限制列表大小(比如只保留最近 100 条消息)。


4. 阻塞版本命令

Redis 提供了 阻塞式 的弹出命令:BLPOPBRPOP

BLPOP / BRPOP

复制代码
BLPOP key [key ...] timeout
BRPOP key [key ...] timeout
  • 如果指定的列表中至少有一个非空 ,则立即从左/右弹出第一个元素,类似 LPOP/RPOP

  • 如果所有列表都为空,则阻塞等待,直到有元素可弹或超时。

  • timeout 为超时时间(秒),如果设为 0 表示永远阻塞。

  • 支持检查多个 key,从左到右扫描,哪个 key 有元素就弹出哪个。

使用场景生产者-消费者模式 。生产者 RPUSH 元素,消费者 BLPOP 阻塞等待,实现一个简单的消息队列。

注意:阻塞版本允许多个消费者同时阻塞在同一个队列上,当有元素时,只有一个消费者能抢到(公平竞争)。


5. 内部编码

Redis 的 List 内部实现会根据元素数量和大小自动选择编码:

  • ziplist(压缩列表) :当列表元素个数 < list-max-ziplist-entries(默认 512)且所有元素长度 < list-max-ziplist-value(默认 64 字节)时使用。节省内存,但读写较大时性能下降。

  • quicklist(快速列表) :从 Redis 3.2 开始,list 统一采用 quicklist 作为内部编码,它是一个双向链表,每个节点是一个 ziplist,兼顾了内存和性能。

你可以用 OBJECT ENCODING key 查看:

复制代码
127.0.0.1:6379> OBJECT ENCODING mylist
"quicklist"

大多数情况下,你无需关心底层编码,Redis 会自动优化。