首页 / Redis 入门教程 / 列表 List

Redis 入门教程

列表 List

本教程共 40 篇 · 第 8 篇 · 更新于 2026-08-02

redislist列表队列阻塞弹出

8. 列表 List

如果说 Hash 适合表达「一个对象」,那 List 适合表达「一个有序的序列」——比如时间线、任务队列、最新评论、消息池。List 里的元素都是字符串,按插入顺序排列,你可以从头部(左)或尾部(右)压入和弹出,也可以按索引随机访问。

本节目标

  • 理解 List 是按插入顺序排序的字符串序列,最多约 42 亿个元素
  • 熟练使用 LPUSH/RPUSH/LRANGE/LPOP/RPOP 维护队列与栈
  • 用阻塞命令 BLPOP/BRPOP 实现可靠的「等待式」消费者
  • LTRIM/LLEN/LINDEX/LINSERT 做裁剪与定位
  • 了解 List 在 Redis 8 的内部编码(listpack / quicklist)与避免「大 key」

8-1 List 是什么

Redis 的 List 是一个按插入顺序排序的字符串序列,底层在 Redis 8 中由 quicklist(由一个个 listpack 片段组成的双向链表)实现,兼顾了头尾操作的 O(1) 与内存紧凑。每个 List 最多可容纳 2³² − 1 个元素(约 42 亿),对绝大多数业务来说就是「无限长」。

需要特别记住:List 里允许重复元素,而且顺序是插入顺序——这和后面要学的 Set(无序、去重)正好相反。

8-2 从两端压入:LPUSH / RPUSH

LPUSH 从**左侧(头部)压入,RPUSH右侧(尾部)**压入,返回值是操作后列表的长度:

127.0.0.1:6379> RPUSH tasks "eat"
(integer) 1
127.0.0.1:6379> RPUSH tasks "sleep" "code"
(integer) 3
127.0.0.1:6379> LPUSH tasks "wakeup"
(integer) 4
127.0.0.1:6379> LRANGE tasks 0 -1
1) "wakeup"
2) "eat"
3) "sleep"
4) "code"

LRANGE key start stop 按索引区间返回元素,索引从 0 开始,-1 表示最后一个元素0 -1 即「全部元素」。注意 LRANGE 不会删除元素,只是「查看」。

8-3 队列与栈:从两端弹出

LPOP 从左侧弹出并返回头部元素,RPOP 从右侧弹出尾部元素:

127.0.0.1:6379> LPOP tasks
"wakeup"
127.0.0.1:6379> RPOP tasks
"code"
127.0.0.1:6379> LRANGE tasks 0 -1
1) "eat"
2) "sleep"

利用「哪头进、哪头出」的组合,List 天然支持两种经典结构:

  • 队列(Queue,先进先出)RPUSH 入队、LPOP 出队——最先放进去的先被消费。
  • 栈(Stack,后进先出)LPUSH 入栈、LPOP 出栈——最后放进去的先弹出。

8-4 阻塞弹出:BLPOP / BRPOP

普通 LPOP/RPOP 在列表为空时会立刻返回 (nil)。如果你的消费者要「一直等直到有数据」,就该用阻塞版 BLPOP / BRPOP

# 终端 A:阻塞等待 tasks 列表,最多等 30 秒
127.0.0.1:6379> BLPOP tasks 30
# (此时没有数据,命令挂起……)

# 终端 B:往里塞一个
127.0.0.1:6379> RPUSH tasks "new-job"
(integer) 1

# 终端 A 立刻收到:
1) "tasks"
2) "new-job"

BLPOP key [key ...] timeout 会一直阻塞,直到任一列表出现可弹出元素或超时(返回 nil)。这对实现任务队列的消费者非常关键:消费者无需轮询,有活儿才被唤醒,既及时又省 CPU。

还可以用 BRPOPLPUSH / BLMOVE 把元素从一个列表原子地弹出并压入另一个列表,常用于「待处理 → 处理中」的可靠转移(若消费者崩溃,元素仍在「处理中」列表,可重试)。

提示BLPOP/BRPOP 的「阻塞」发生在 Redis 服务端,不会占用你客户端的计算资源;超时参数以秒为单位,设为 0 表示无限等待。生产队列务必用阻塞命令,而不是循环 LPOP 轮询。

8-5 裁剪、定位与计数

  • LLEN key:列表长度。
  • LINDEX key index:按索引取元素(如 LINDEX tasks 0 取头,-1 取尾),但它是 O(N),大列表慎用。
  • LINSERT key BEFORE|AFTER pivot value:在指定元素前/后插入。
  • LSET key index value:按索引改值。
  • LREM key count value:删除指定数量的某值(count>0 从头删、count<0 从尾删、count=0 删全部)。
  • LTRIM key start stop:只保留区间内的元素,其余删除——常被用来做「只保留最新 N 条」。
# 只保留最新 100 条,其余裁掉(经典「最新列表」做法)
127.0.0.1:6379> LTRIM feed 0 99
OK
127.0.0.1:6379> LLEN feed
(integer) 100

此外,LPOS key element 可以返回某元素在列表中的索引位置(支持 RANK/COUNT 找多个匹配);LMPOP 则能一次从多个列表中弹出,适合「优先队列」等更高级的玩法。这些命令让 List 在「有序序列」这一基础能力之上,还能灵活支撑搜索、优先级调度等需求。

8-6 实战:用 List 做最新动态 / 任务队列

场景一:最新评论列表

127.0.0.1:6379> LPUSH comments:post:42 "用户A:写得好"
(integer) 1
127.0.0.1:6379> LPUSH comments:post:42 "用户B:学到了"
(integer) 2
# 取最新 10 条展示
127.0.0.1:6379> LRANGE comments:post:42 0 9
1) "用户B:学到了"
2) "用户A:写得好"

场景二:简单任务队列

# 生产者投递任务
127.0.0.1:6379> RPUSH jobs "send-email:1001"
(integer) 1
# 消费者阻塞取任务
127.0.0.1:6379> BLPOP jobs 30
1) "jobs"
2) "send-email:1001"

提示:List 做队列够用且简单,但若你需要「消费组、消息确认、多次投递、按时间随机读取」等更完备的能力,请考虑 Stream 类型(本教程有专门章节)。List 队列适合轻量、单消费者的场景。

8-7 编码与内存(了解即可)

和 Hash 类似,Redis 8 里的小 List 用紧凑的 listpack 编码(早期版本曾用 ziplist,7.0 起被取代),多个 listpack 再串成 quicklist。相关阈值配置如 list-max-listpack-size(控制单个片段大小)会自动起作用,你一般无需改动。知道「小列表特别省内存」即可。

8-8 元素的可靠转移:LMOVE / BLMOVE

除了 RPOPLPUSH 这种老命令,Redis 提供了更通用的 LMOVE 在两个列表之间原子地转移元素,并支持阻塞版本 BLMOVE

127.0.0.1:6379> RPUSH jobs:todo "task-1"
(integer) 1
# 把 jobs:todo 的右侧元素原子地移到 jobs:doing 的左侧
127.0.0.1:6379> LMOVE jobs:todo jobs:doing RIGHT LEFT
"task-1"
127.0.0.1:6379> LRANGE jobs:doing 0 -1
1) "task-1"

LMOVE source destination LEFT|RIGHT LEFT|RIGHT 通过四个位置参数可以组合出「队列→栈」「栈→栈」等多种转移语义,常用于「待处理 → 处理中 → 完成」的流水线。配合 BLMOVE 的阻塞能力,消费者能在「处理中」列表为空时挂起等待,比手写轮询健壮得多。

8-9 常见误区

  • 误区:List 会去重。 不会,List 允许重复元素,且严格保序;要去重用 Set。
  • 误区:LRANGE 0 -1 随便看大列表。 列表很大时一次性取全部会占用大量网络与内存;展示场景用分页(如每次 LRANGE 0 99),运维遍历用专门的 redis-cli --bigkeys
  • 误区:LINDEXGET 一样快。 LINDEX 是 O(N),定位靠后元素慢;高频随机访问应考虑其他结构。
  • 误区:队列用 LPOP 轮询。 空列表会立刻返回,轮询浪费资源;用 BLPOP/BRPOP 阻塞等待更优。

8-10 小结

  • List 是按插入顺序排序的字符串序列,允许重复,最多约 42 亿个元素。
  • 压入:LPUSH(头)/RPUSH(尾);弹出:LPOP(头)/RPOP(尾);查看:LRANGE
  • 队列 = RPUSH+LPOP,栈 = LPUSH+LPOP;可靠等待用阻塞版 BLPOP/BRPOP
  • 裁剪定位:LTRIM/LINSERT/LSET/LREM/LLEN/LINDEX
  • 轻量队列、最新列表首选 List;需要消费组与持久化消息流时用 Stream。
  • 底层编码为 quicklist(listpack 片段),小列表内存紧凑。

提示:版本基线说明。本教程正文统一以 Redis 8.x(最新稳定版) 表述。需要留意:官方 redis.io 下载页目前把 8.8 标注为 “Latest stable”,而 GitHub 上 redis/redis 的最新发布标签为 8.10.0;二者同属 8.x 大版本,命令与数据类型差异极小,本教程以 8.10.0 为准。

下一章我们看 Set(集合)——无序、唯一,天生适合去重与「共同好友」这类集合运算。