Redis命令:BLPOP

Redis BLPOP 命令详细教程

BLPOP 是列表的阻塞式弹出原语,也是 LPOP 的阻塞版本。它按给定顺序检查多个列表,从第一个非空列表的表头弹出一个元素;若所有列表都为空,则阻塞连接直到有元素可用或超时。它从 Redis 2.0.0 起提供,是阻塞队列消费的经典命令。

资料合集:https://pan.quark.cn/s/10e98d308913、https://pan.quark.cn/s/f56bc69c5338

一、概览与语法

redis 复制代码
BLPOP key [key ...] timeout
项目 说明
数据类型 List
支持版本 Redis 2.0.0 起;6.0.0 起 timeout 解释为 double
key 一个或多个列表 Key,按书写顺序检查
timeout 最长阻塞秒数,double 值;0 表示无限阻塞
返回值 两元素数组:第一个是被弹出元素所在的 Key,第二个是元素值;超时返回空值
时间复杂度 O(N),N 为提供的 Key 数量
ACL @write、@list、@slow、@blocking
命令标记 write、blocking

官方说明:元素从第一个非空列表的表头弹出,Key 按传入顺序依次检查。若所有指定 Key 都不存在,命令阻塞连接,直到另一个客户端对其中某个 Key 执行 LPUSH 或 RPUSH;超时则返回空值。$TRAE_REF

如果弹出的是该列表的最后一个元素,这个 Key 会被删除。

二、基础示例

以下命令在测试实例的 redis-cli 中执行。文中结果是预期说明,未实际连接 Redis 运行。示例沿用官方文档的数据与结果。

redis 复制代码
DEL tutorial:{blpop}:list1 tutorial:{blpop}:list2
RPUSH tutorial:{blpop}:list1 a b c
BLPOP tutorial:{blpop}:list1 tutorial:{blpop}:list2 0

预期结果:RPUSH 返回 3;BLPOP 返回一个两元素数组,第一个元素是 "tutorial:{blpop}:list1",第二个是 "a"。因为 list1 非空,命令立即返回,没有阻塞。

BLPOP 从表头(左侧)弹出,与 LPOP 的方向一致。若需要从表尾弹出,应使用 BRPOP。

三、非阻塞行为与多 Key 顺序

只要至少一个指定 Key 持有非空列表,BLPOP 就立即从其中第一个非空列表的表头弹出元素,并连同该 Key 一起返回。

Key 严格按书写顺序检查。假设 k1 不存在,k2 与 k3 都非空:

redis 复制代码
DEL tutorial:{blpop}:k1 tutorial:{blpop}:k2 tutorial:{blpop}:k3
RPUSH tutorial:{blpop}:k2 b2
RPUSH tutorial:{blpop}:k3 c3
BLPOP tutorial:{blpop}:k1 tutorial:{blpop}:k2 tutorial:{blpop}:k3 0

预期结果:返回 ["tutorial:{blpop}:k2", "b2"]。虽然 k3 也非空,但 k2 在参数中更靠前,因此优先返回 k2 的元素。

这一规则对"多个 Key 同时变为非空"的情形同样成立:当一次命令、事务或脚本让多个 Key 同时有数据时,客户端按自己 BLPOP 调用中的 Key 顺序 被服务,而不是按写入发生的顺序。官方给出的例子中,客户端阻塞在 BLPOP key1 key2 0,另一客户端在事务里先 RPUSH key2 再 RPUSH key1,事务结束后该客户端仍从 key1 弹出,因为 key1 在它的参数里更靠前。$TRAE_REF

四、阻塞行为与优先级

当所有指定 Key 都不存在(或都为空)时,BLPOP 会挂起当前连接,不占用 CPU,直到:

  • 其他客户端对其中某个 Key 执行 LPUSH 或 RPUSH,此时返回被唤醒的 Key 与弹出的元素;
  • 指定的 timeout 到期,此时返回空值。

需要两个客户端配合才能观察阻塞效果。在终端 A 执行:

redis 复制代码
DEL tutorial:{blpop}:q
BLPOP tutorial:{blpop}:q 0

终端 A 会一直挂起。在终端 B 执行:

redis 复制代码
RPUSH tutorial:{blpop}:q job1

终端 A 立即返回 ["tutorial:{blpop}:q", "job1"],终端 B 的 RPUSH 返回 1。

关于"谁先被服务",官方明确了几条优先级规则:多个客户端阻塞在同一个 Key 上时,等待时间最长的客户端先被服务;客户端一旦被唤醒就不再保留优先级,下次再阻塞时按新的排队位置处理。$TRAE_REF

五、多元素推送时的服务顺序

当一条命令一次性向列表推入多个元素(如 LPUSH mylist a b c),阻塞客户端的服务时机在 Redis 2.6 之后发生了变化。

版本 行为
Redis 2.6 及以后 先完整执行推送命令,再服务阻塞客户端
Redis 2.4 在推送过程中即服务阻塞客户端

以"客户端 A 阻塞在 BLPOP foo 0,客户端 B 执行 LPUSH foo a b c"为例:Redis 2.6 及以后,推送完成后列表为 c,b,a,从左侧弹出得到 c,因此客户端 A 收到 "c"。Redis 2.4 则会在推入第一个元素时立即服务,客户端 A 收到 "a"。官方指出 2.4 的行为在复制与 AOF 持久化时问题较多,2.6 起改为更简单一致的语义。$TRAE_REF

同理,如果一条命令、事务或脚本在推入元素后又删除了该列表,阻塞客户端不会被服务,会继续阻塞直到列表中重新有数据。

六、timeout 参数

timeout 是 double 类型的秒数,支持小数。

timeout 取值 行为
0 无限阻塞,直到有元素可用
正数(如 5) 最多阻塞 5 秒,超时返回空值
小数(如 0.1) 最多阻塞 100 毫秒
负数 报错 ERR timeout is negative
redis 复制代码
DEL tutorial:{blpop}:empty
BLPOP tutorial:{blpop}:empty 1

预期结果:约 1 秒后返回 (nil),且不会创建该 Key。

超时返回的是空值而不是错误,业务代码必须显式判断空值。需要注意的是,超时值只是"最长等待时间"的上界,实际返回可能因调度略有延迟。

七、在 MULTI/EXEC 与脚本中的行为

在 MULTI/EXEC 事务块中使用 BLPOP 意义不大:若真正阻塞,会导致整个服务器被占用而无法让其他客户端推送数据。因此官方规定,当事务中的列表为空时,BLPOP 直接返回空值,与超时的表现相同,不会阻塞 。$TRAE_REF

redis 复制代码
DEL tutorial:{blpop}:t
MULTI
BLPOP tutorial:{blpop}:t 0
EXEC

预期结果:EXEC 返回一个只含空值的数组,命令立即完成,没有阻塞。这可以理解为"事务内时间以无限速度流逝"。

八、可靠队列与事件通知模式

BLPOP 在返回元素的同时会把它从列表移除,元素只存在于客户端上下文中。如果客户端在处理途中崩溃,该元素就永久丢失了。这正是"非可靠队列"的由来。

若需要更可靠的消息处理,应改用 BRPOPLPUSH 或 BLMOVE:它们在返回元素前先把元素推入一个"处理中"列表,消费者处理完成后再用 LREM 移除;即使消费者崩溃,元素仍留在处理中列表里可被重新投递。$TRAE_REF

另一个常见模式是事件通知 :Redis 没有阻塞版的 SPOP,但可以借助辅助列表实现"阻塞等待集合新增元素"。消费者循环处理集合元素,处理完后 BRPOP helper_key 等待;生产者在事务中先 SADD 目标集合,再 LPUSH 辅助列表作为信号。$TRAE_REF

redis 复制代码
DEL tutorial:{blpop}:set tutorial:{blpop}:helper
SADD tutorial:{blpop}:set e1
LPUSH tutorial:{blpop}:helper x
BLPOP tutorial:{blpop}:helper 0
SMEMBERS tutorial:{blpop}:set

预期结果:BLPOP 返回 ["tutorial:{blpop}:helper", "x"],SMEMBERS 返回集合中待处理的元素。

九、与相近命令的区别

命令 弹出方向 是否阻塞 是否转发到目标列表 返回值形态
BLPOP 表头(左侧) 是 否 Key, 元素 数组
BRPOP 表尾(右侧) 是 否 Key, 元素 数组
LPOP 表头(左侧) 否 否 元素或空值
BRPOPLPUSH 表尾(右侧) 是 是,推入目标左侧 元素
BLMOVE 两端可选 是 是,方向可配 元素

BLPOP 返回的是"Key + 元素"的数组,因为一次可监听多个 Key,必须告诉调用方元素来自哪个 Key;而 BRPOPLPUSH、BLMOVE 只处理一个源 Key,因此只返回元素本身。

十、边界情况与错误处理

场景 行为
所有 Key 都不存在或为空 阻塞,直到有元素或超时;超时返回空值
弹出最后一个元素 该 Key 被删除
多个 Key 同时非空 返回参数中最靠前的非空 Key 的元素
某个 Key 类型不是 List 报 WRONGTYPE
timeout 为负数 报错 ERR timeout is negative
timeout 不是数字 报参数类型错误
未提供任何 Key 报语法错误(至少需要一个 Key 加 timeout)
redis 复制代码
SET tutorial:{blpop}:str text
BLPOP tutorial:{blpop}:str 0
BLPOP tutorial:{blpop}:empty -1

预期结果:第一条在 String 类型上报 WRONGTYPE,且类型检查先于阻塞,不会挂起;第二条报 ERR timeout is negative。

需要说明的是,若列表中混合了非 List 类型与合法 List,BLPOP 会在检查到非 List 类型时报 WRONGTYPE,而不会跳过它去处理后面的合法列表。

十一、客户端示例

前提为已安装 redis-py 并准备好本地测试实例。

python 复制代码
import redis

r = redis.Redis(host="localhost", port=6379, decode_responses=True)
k1 = "tutorial:{blpop}:py1"
k2 = "tutorial:{blpop}:py2"
try:
    r.delete(k1, k2)
    r.rpush(k1, "a", "b", "c")

    # 有元素时立即返回 (key, value)
    print(r.blpop([k1, k2], timeout=0))     # ('tutorial:{blpop}:py1', 'a')

    # 全部为空且超时,返回 None
    r.delete(k1)
    print(r.blpop([k1, k2], timeout=1))     # None
finally:
    r.delete(k1, k2)
    r.close()

Java(Jedis)示例,返回 List:

java 复制代码
try (Jedis jedis = new Jedis("localhost", 6379)) {
    String k1 = "tutorial:{blpop}:java1";
    String k2 = "tutorial:{blpop}:java2";
    jedis.del(k1, k2);
    jedis.rpush(k1, "a", "b", "c");

    // 有元素时返回 [key, value]
    System.out.println(jedis.blpop(0, k1, k2));
    // [tutorial:{blpop}:java1, a]
    jedis.del(k1, k2);
}

十二、集群、连接与并发注意事项

集群模式 :BLPOP 可监听多个 Key,在 Redis Cluster 中所有 Key 必须落在同一个哈希槽,否则报 CROSSSLOT。使用哈希标签可让多个 Key 同槽,例如 tutorial:{blpop}:q1 与 tutorial:{blpop}:q2 会落到同一槽。$TRAE_REF

Pipeline:BLPOP 可以与管道一起使用,但通常只在管道最后一条命令时才有意义,否则前面的命令要等它返回才能继续。

原子性与可靠性:单条 BLPOP 的弹出是原子的,不会出现元素既在列表又被弹出的中间状态。但"弹出---处理"整体不是原子的,处理失败时元素已经丢失,需要可靠语义时改用 BRPOPLPUSH/BLMOVE 的处理中列表方案。

连接占用:无限阻塞会长期占用一个连接,高并发下应结合业务容忍度设置有限 timeout,并处理空值,避免连接池被阻塞命令耗尽。

十三、练习、排错与总结

练习:新建 tutorial:{blpop}:ex1 与 tutorial:{blpop}:ex2,用 RPUSH 向 ex1 写入 a、b、c;执行 BLPOP ex1 ex2 0,确认返回 ex1 与 "a";再对两个空 Key 执行 BLPOP ex1 ex2 1,确认约 1 秒后返回空值;最后在 MULTI/EXEC 中执行 BLPOP,确认不阻塞而直接返回空值。

排错要点:命令一直挂起说明所有 Key 为空且 timeout 为 0,可改用有限 timeout 或确认生产者是否已推送;返回空值表示超时而非错误;返回的 Key 不是预期项时检查参数顺序,BLPOP 按书写顺序取第一个非空列表;报 CROSSSLOT 时在集群中为多个 Key 加相同哈希标签;报 WRONGTYPE 时用 TYPE 检查类型;事务中不阻塞是预期行为。清理使用 DEL tutorial:{blpop}:list1 tutorial:{blpop}:list2 tutorial:{blpop}:k1 tutorial:{blpop}:k2 tutorial:{blpop}:k3 tutorial:{blpop}:q tutorial:{blpop}:empty tutorial:{blpop}:t tutorial:{blpop}:set tutorial:{blpop}:helper tutorial:{blpop}:str tutorial:{blpop}:ex1 tutorial:{blpop}:ex2。速记:2.0 起支持、LPOP 的阻塞版、从表头弹出、可监听多个 Key 且按顺序检查、超时返回空值、弹出最后一个元素会删除 Key、事务中不阻塞、元素取出即移出故非可靠队列。

相关推荐
敲个大西瓜3 小时前
langchain笔记(一)
redis·笔记·langchain
小小龙学IT8 小时前
C++ Redis 客户端深度解析(hiredis 与 redis-plus-plus)——工业数采实时缓存 C++ 侧实战
c++·redis·缓存
mengge.cloud8 小时前
Redis
运维·服务器·数据库·redis·缓存·云计算
欣栀♡8 小时前
Redis 缓存穿透、击穿、雪崩,背出定义只是及格线
数据库·redis·缓存
codigger9 小时前
Redis 正式接入 AI:当"最懂速度的数据库"开始解决"记忆问题"
redis·分布式·后端·ai·向量检索
禾小西9 小时前
08丨Redis 哨兵集群:哨兵故障后还能完成切换吗?
数据库·redis·缓存
挖掘狂人9 小时前
Redis 正式接入 AI:当"最懂速度的数据库"开始解决"记忆问题"
人工智能·redis·后端
阿标在干嘛9 小时前
缓存架构的“场景适配“:我们为什么在部分服务中采用了本地缓存?
缓存·架构
需要8269 小时前
缓存与数据库一致性:延迟双删与订阅 binlog 的取舍
数据库·缓存