列表 List
本教程共 40 篇 · 第 8 篇 · 更新于 2026-08-02
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。 - 误区:
LINDEX和GET一样快。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(集合)——无序、唯一,天生适合去重与「共同好友」这类集合运算。