45、Redis列表操作:LPUSH/RPUSH、LPOP/RPOP、LRANGE、LLEN、BLPOP/BRPOP阻塞操作

Redis的列表(List)是我在项目中用得最多的数据结构之一。说白了,它就是一个双向链表,可以从左边推,也可以从右边推,两边都能弹出。这种设计让它在消息队列、任务调度、最新消息列表等场景下特别好用。

今天咱们就把Redis列表的常用操作捋一遍。我会结合自己踩过的坑,给你讲清楚每个命令的用法和注意事项。

核心操作:LPUSH与RPUSH

这两个命令是往列表里塞数据的。LPUSH是从左边插入,RPUSH是从右边插入。我习惯把列表想象成一个管道,左边是入口,右边也是入口,但方向不同。

// LPUSH:从左边插入一个或多个元素
LPUSH mylist "hello"
LPUSH mylist "world" "redis"

// RPUSH:从右边插入一个或多个元素
RPUSH mylist "a" "b" "c"

嗯,这里有个细节要注意:LPUSH是头插法,RPUSH是尾插法。如果你用LPUSH连续插入多个值,顺序是反的。比如上面LPUSH "world" "redis",实际列表里是"redis"在左边,"world"在右边。

我的习惯:做消息队列时,我一般用RPUSH生产消息,LPOP消费消息。这样符合先进先出的逻辑。如果你需要后进先出,那就LPUSH配合LPOP。

弹出操作:LPOP与RPOP

有进就有出。LPOP从左边弹出一个元素,RPOP从右边弹出。弹出后元素就从列表里删除了。

// 从左边弹出一个元素
LPOP mylist

// 从右边弹出一个元素
RPOP mylist

// 可以指定弹出个数(Redis 6.2+)
LPOP mylist 2

我曾经在项目里犯过一个低级错误:用LPOP取数据,但忘了检查返回值。如果列表为空,LPOP返回nil,程序直接崩了。所以记住,一定要判空

避坑指南:我曾经在生产环境遇到过一个问题——多个消费者同时LPOP同一个列表,导致数据竞争。后来改用BRPOP阻塞读取才解决。后面会讲。

查看操作:LRANGE与LLEN

LRANGE用来查看列表的一部分,LLEN返回列表长度。这两个命令不会修改数据,只是读取。

// 查看整个列表
LRANGE mylist 0 -1

// 查看前10个元素
LRANGE mylist 0 9

// 获取列表长度
LLEN mylist

LRANGE的索引是从0开始的,-1表示最后一个元素。这个命令的时间复杂度是O(N),N是返回的元素个数。如果你的列表特别长,比如几十万条,一次LRANGE全部会卡住Redis。我建议分页取,比如一次取100条。

命令 时间复杂度 注意事项
LPUSH/RPUSH O(1) 每个元素 批量插入时注意顺序
LPOP/RPOP O(1) 返回nil时需处理
LRANGE O(N) 避免取全部,建议分页
LLEN O(1) Redis内部维护了长度

阻塞操作:BLPOP与BRPOP

这是Redis列表的杀手锏。BLPOP和BRPOP是LPOP和RPOP的阻塞版本。如果列表为空,它们不会立即返回nil,而是阻塞等待,直到有数据或者超时。

// 阻塞从左边弹出,超时时间10秒
BLPOP mylist 10

// 阻塞从右边弹出,超时时间0表示无限等待
BRPOP mylist 0

// 可以同时监听多个列表
BLPOP list1 list2 list3 5

你想想看,这个特性多适合做消息队列。生产者往列表里RPUSH消息,消费者用BRPOP阻塞等待。没有消息时消费者就挂在那,不浪费CPU。消息一来立刻处理。

核心要点:BLPOP/BRPOP的超时时间单位是秒。设为0表示永远阻塞,直到有数据。如果超时返回nil,你需要决定是重试还是退出。

我记得有一次线上事故,就是因为超时时间设得太短,消费者频繁重试,把Redis的连接数打满了。后来改成5秒超时,问题就解决了。

知识体系总览

下面这张图把Redis列表的核心操作串起来了。你可以看到,左边和右边是对称的,阻塞操作是普通操作的增强版。

Redis 列表操作知识体系 Redis List(双向链表) LPUSH / LPOP BLPOP(阻塞版) RPUSH / RPOP BRPOP(阻塞版) LRANGE / LLEN 左进左出 = 栈(LIFO) | 右进左出 = 队列(FIFO) | 阻塞版适合消息队列

实际应用场景

说了这么多理论,咱们看看实际怎么用。我挑三个最常见的场景:

  • 消息队列:生产者RPUSH,消费者BRPOP。这是最经典的用法,解耦、异步、削峰。
  • 最新消息列表:比如用户的最新动态,用LPUSH插入,LRANGE 0 9取前10条。注意要控制列表长度,别让它无限增长。
  • 任务调度:把任务塞进列表,多个worker用BRPOP抢任务。谁抢到谁处理。
小技巧:如果你需要限制列表长度,可以用LTRIM命令。比如每次LPUSH后执行LTRIM mylist 0 99,只保留最近100条。这样列表不会撑爆内存。

性能与注意事项

Redis列表的操作大部分是O(1)的,但LRANGE是O(N)。如果你的列表有几十万条,一次LRANGE全部会阻塞Redis几秒钟。我建议用SCAN或者分页的方式取数据。

另外,阻塞操作BLPOP/BRPOP虽然好用,但要注意连接数。每个阻塞的客户端都会占用一个Redis连接。如果客户端太多,连接数会飙升。我一般限制最大连接数,或者用连接池。

嗯,最后说一句:Redis列表虽然简单,但用好了能解决很多实际问题。别小看它。


公众号:蓝海资料掘金营,微信deep3321