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、事务中不阻塞、元素取出即移出故非可靠队列。