BLPOP
BLPOP key [key ...] timeout
- 可用版本
- Redis 开源版 2.0.0
- 时间复杂度
- O(N),其中 N 是提供的键的数量。
- ACL 类别
-
@write
,@list
,@slow
,@blocking
,
BLPOP
是一个阻塞的列表弹出原语。它是 LPOP
的阻塞版本,因为当给定的任何列表都没有元素可弹出时,它会阻塞连接。元素会从第一个非空列表的头部弹出,并按照给定的顺序检查键。该命令会返回被弹出元素的键和值。
非阻塞行为
调用 BLPOP
时,如果至少有一个指定的键包含非空列表,则会从该列表的头部弹出一个元素,并将其与弹出的 key
一起返回给调用者。
键会按照给定的顺序检查。假设键 list1
不存在,而 list2
和 list3
包含非空列表。考虑以下命令:
BLPOP list1 list2 list3 0
BLPOP
保证从存储在 list2
的列表中返回一个元素(因为在按照 list1
、list2
和 list3
的顺序检查时,它是第一个非空列表)。
阻塞行为
如果所有指定的键都不存在,BLPOP
将阻塞连接,直到另一个客户端对其中一个键执行 LPUSH
或 RPUSH
操作。
一旦其中一个列表上出现新数据,客户端将返回解除其阻塞的键名和弹出的值。
当 BLPOP
导致客户端阻塞并且指定了非零超时时,如果指定的超时时间内没有对任何一个指定键执行推送操作,客户端将解除阻塞并返回一个 nil
多批量值。
超时参数被解释为一个双精度浮点值,指定阻塞的最大秒数。可以使用零作为超时值来无限期阻塞。
哪个键优先提供服务?哪个客户端?哪个元素?优先级排序细节。
- 如果客户端尝试阻塞多个键,但至少有一个键包含元素,则返回的键/元素对是第一个从左到右且具有一个或多个元素的键。在这种情况下,客户端不会被阻塞。例如,
BLPOP key1 key2 key3 key4 0
,假设key2
和key4
都非空,将始终返回来自key2
的元素。 - 如果多个客户端阻塞在同一个键上,则优先提供服务的客户端是等待时间最长的客户端(第一个阻塞在该键上的客户端)。一旦客户端解除阻塞,它不再保留任何优先级;当它在下一次调用
BLPOP
时再次阻塞时,它将根据已阻塞在同一键上的客户端数量来获得服务,这些客户端都将先于它获得服务(从第一个阻塞的到最后一个阻塞的)。 - 当一个客户端同时阻塞在多个键上,并且多个键同时有可用元素(因为事务或 Lua 脚本向多个列表添加了元素),客户端将通过收到推送操作的第一个键解除阻塞(假设它有足够的元素来服务我们的客户端,因为可能有其他客户端也在等待这个键)。基本上,在每次命令执行后,Redis 会运行一个列表,包含所有收到数据且至少有一个客户端阻塞的键。这个列表按照新元素到达时间排序,从第一个收到数据的键到最后一个。对于每个处理的键,只要该键中有元素,Redis 就会以 FIFO 方式服务所有等待该键的客户端。当键为空或不再有等待该键的客户端时,处理前一个命令/事务/脚本中收到新数据的下一个键,以此类推。
当向列表中推送多个元素时 BLPOP
的行为。
有时,列表可以在同一概念命令的上下文中接收多个元素:
当向有客户端阻塞的列表中推送多个元素时,Redis 2.4 和 Redis 2.6 或更新版本的行为是不同的。
对于 Redis 2.6,执行多次推送的命令会先完成,然后 *仅在* 命令执行完成后,被阻塞的客户端才会得到服务。考虑以下命令序列。
Client A: BLPOP foo 0
Client B: LPUSH foo a b c
如果在 Redis 2.6 或更高版本的服务器上发生上述情况,客户端 **A** 将获得元素 c
,因为在 LPUSH
命令执行后,列表包含 c,b,a
,因此从左侧取出一个元素意味着返回 c
。
相反,Redis 2.4 的工作方式不同:客户端是在推送操作的 *上下文中* 得到服务的,所以当 LPUSH foo a b c
开始向列表推送第一个元素时,它将立即传递给客户端 **A**,客户端将收到 a
(第一个推送的元素)。
Redis 2.4 的行为在数据复制或持久化到 AOF 文件时会产生很多问题,因此 Redis 2.6 引入了更通用和语义上更简单的行为以防止出现问题。
请注意,出于同样的原因,Lua 脚本或 MULTI/EXEC
块可能会先向列表推送元素,然后 **删除列表**。在这种情况下,被阻塞的客户端将完全不会得到服务,并且只要在单个命令、事务或脚本执行后列表中没有数据,它们就会继续被阻塞。
在 MULTI
/ EXEC
事务中使用 BLPOP
BLPOP
可以与流水线结合使用(发送多个命令并批量读取回复),但这种设置几乎只有在它作为流水线的最后一个命令时才有意义。
在 MULTI
/ EXEC
块中使用 BLPOP
意义不大,因为它需要阻塞整个服务器来原子地执行该块,这反过来又不允许其他客户端执行推送操作。因此,在列表为空时,MULTI
/ EXEC
块中 BLPOP
的行为是返回一个 nil
多批量回复,这与达到超时时发生的情况相同。
如果你喜欢科幻小说,可以想象在 MULTI
/ EXEC
块中时间以无限速度流逝...
示例
redis> DEL list1 list2
(integer) 0
redis> RPUSH list1 a b c
(integer) 3
redis> BLPOP list1 list2 0
1) "list1"
2) "a"
可靠队列
当 BLPOP
向客户端返回一个元素时,它也会从列表中移除该元素。这意味着该元素仅存在于客户端的上下文中:如果客户端在处理返回的元素时崩溃,该元素将永远丢失。
对于某些需要更可靠消息系统的应用来说,这可能是一个问题。在这种情况下,请查看 BRPOPLPUSH
命令,它是 BLPOP
的变体,它在将返回的元素返回给客户端之前,会将其添加到目标列表。
模式:事件通知
使用阻塞列表操作可以实现不同的阻塞原语。例如,对于某些应用,您可能需要阻塞等待 Redis Set 中的元素,以便在新元素添加到 Set 后,可以立即检索它而无需轮询。这需要一个阻塞版本的 SPOP
,虽然没有直接提供,但使用阻塞列表操作可以轻松实现此任务。
消费者将执行
LOOP forever
WHILE SPOP(key) returns elements
... process elements ...
END
BRPOP helper_key
END
而生产者端将只使用
MULTI
SADD key element
LPUSH helper_key x
EXEC
RESP2 回复
以下之一
- 空值回复(Nil reply):没有元素可以弹出且超时已到。
- 数组回复(Array reply):弹出元素的键和弹出元素的值。
RESP3 回复
以下之一
- 空值回复(Null reply):没有元素可以弹出且超时已到。
- 数组回复(Array reply):弹出元素的键和弹出元素的值。
历史版本
- 自 Redis 6.0.0 版本起:
timeout
被解释为双精度浮点数而非整数。