跳到主要内容

2.4 列表 List

List 是一个有序、可重复的字符串列表,两端都能插入和弹出。用它可以做队列、栈、"最新 N 条"列表。

1. List 是什么

想象一根两端开口的管子,元素按插入顺序排列:

左边(L) ←→ [ a, b, c, d ] ←→ 右边(R)
  • 有序:按你插入的顺序保存(不是按大小排序)
  • 可重复:同一个值可以出现多次
  • 两端都可以 push(塞入)和 pop(弹出),所以能同时模拟队列和栈

2. 基础操作

List 命令以 l(left / list)或 r(right)开头:

# 从左边压入(注意:多个元素时是逐个从左塞入)
127.0.0.1:6379> lpush mylist a b c
(integer) 3
# 过程:[a] → [b,a] → [c,b,a],所以现在列表是 c,b,a

# 从右边压入
127.0.0.1:6379> rpush mylist x
(integer) 4
# 现在是 [c, b, a, x]

# 查看范围(0 是第一个,-1 是最后一个,lrange key 0 -1 = 查看全部)
127.0.0.1:6379> lrange mylist 0 -1
1) "c"
2) "b"
3) "a"
4) "x"

# 从左弹出 / 从右弹出(取出并删除)
127.0.0.1:6379> lpop mylist
"c"
127.0.0.1:6379> rpop mylist
"x"

# 长度
127.0.0.1:6379> llen mylist
(integer) 2

# 按下标取单个元素(不删除)
127.0.0.1:6379> lindex mylist 0
"b"

💡 记忆技巧:lrange 的下标支持负数,-1 是最后一个、-2 是倒数第二个,和 Python 切片一样。

3. 用 List 实现队列和栈

这是理解 List 的关键,就两个公式:

队列(先进先出 FIFO):lpush + rpop (左边进,右边出)
栈 (后进先出 LIFO):lpush + lpop (左边进,左边出)

动手体验队列:

127.0.0.1:6379> lpush queue 任务1
(integer) 1
127.0.0.1:6379> lpush queue 任务2
(integer) 2
127.0.0.1:6379> rpop queue
"任务1" # 先进来的先出去 ✓
127.0.0.1:6379> rpop queue
"任务2"

4. 阻塞弹出:BLPOP / BRPOP

做消息队列时有个问题:消费者怎么知道队列里有没有新任务?总不能写死循环不停地 rpop 吧(这叫轮询,浪费资源)。

Redis 提供了阻塞版的 pop:队列为空时,连接会原地等待,直到有新元素进来或超时:

# 等待 queue 出现元素,最多等 30 秒(0 表示无限等待)
127.0.0.1:6379> brpop queue 30
(此处会卡住...直到另一个客户端 lpush queue 新任务)
1) "queue"
2) "新任务"

可以开两个 redis-cli 窗口试试:一个执行 brpop queue 0 等着,另一个 lpush queue hello,观察第一个窗口立刻收到数据。这就是一个最简单的生产者-消费者模型。

5. 其他实用命令

# 只保留指定范围,其余删除(做"最新 N 条"的关键)
127.0.0.1:6379> ltrim mylist 0 99 # 只留前 100 个

# 删除值等于 x 的元素:count>0 从左删 count 个,<0 从右删,=0 删所有
127.0.0.1:6379> lrem mylist 1 "b"

# 修改指定下标的值
127.0.0.1:6379> lset mylist 0 "newvalue"

6. 典型应用场景

场景一:最新动态列表(朋友圈 / 微博时间线)

新内容从左边进,永远只保留最新 100 条:

lpush user:1001:feed "动态ID:9527"
ltrim user:1001:feed 0 99 # 修剪,防止无限增长
lrange user:1001:feed 0 9 # 取最新 10 条展示

场景二:简易任务队列

# 生产者(Web 服务):用户下单后把任务扔进队列
lpush task:send_email '{"to":"a@b.com","subject":"订单确认"}'

# 消费者(后台进程):阻塞式取任务处理
brpop task:send_email 0

💡 List 做队列简单好用,但没有 ack 确认机制——消费者取走任务后如果崩溃了,任务就丢了。对可靠性要求高的场景用 Redis Stream 或专业消息队列(RabbitMQ/Kafka)。

7. 动手练习

  1. lpush + ltrim + lrange 模拟"浏览历史":依次浏览商品 101~105,只保留最近 3 条,查看结果应为 105、104、103
  2. 开两个终端窗口,体验 brpop 的阻塞效果
  3. 思考:rpush + lpop 也是队列吗?(是的,方向反过来而已)

8. 小结

命令作用
lpush / rpush左 / 右压入
lpop / rpop左 / 右弹出
blpop / brpop阻塞式弹出(做队列用)
lrange key 0 -1查看全部元素
ltrim只保留指定范围
llen / lindex / lrem长度 / 按下标取 / 删除指定值

记住两个公式:lpush + rpop = 队列,lpush + lpop = 栈。


下一章:天生去重的集合 👉 2.5 集合 Set