Redis命令之BLPOP

BLPOP key [key ...] timeout

起始版本:2.0.0

时间复杂度:O(1)

BLPOP 是阻塞式列表的弹出原语。 它是命令 LPOP 的阻塞版本,这是因为当给定列表内没有任何元素可供弹出的时候, 连接将被 BLPOP 命令阻塞。 当给定多个 key 参数时,按参数 key 的先后顺序依次检查各个列表,弹出第一个非空列表的头元素。

非阻塞行为

BLPOP 被调用时,如果给定 key 内至少有一个非空列表,那么弹出遇到的第一个非空列表的头元素,并和被弹出元素所属的列表的名字 key 一起,组成结果返回给调用者。

当存在多个给定 key 时, BLPOP 按给定 key 参数排列的先后顺序,依次检查各个列表。 我们假设 key list1 不存在,而 list2 和 list3 都是非空列表。考虑以下的命令:

BLPOP list1 list2 list3 0

BLPOP 保证返回一个存在于 list2 里的元素(因为它是从 list1 –> list2 –> list3 这个顺序查起的第一个非空列表)。

阻塞行为

如果所有给定 key 都不存在或包含空列表,那么 BLPOP 命令将阻塞连接, 直到有另一个客户端对给定的这些 key 的任意一个执行 LPUSHRPUSH 命令为止。

一旦有新的数据出现在其中一个列表里,那么这个命令会解除阻塞状态,并且返回 key 和弹出的元素值。

BLPOP 命令引起客户端阻塞并且设置了一个非零的超时参数 timeout 的时候, 若经过了指定的 timeout 仍没有出现一个针对某一特定 key 的 push 操作,则客户端会解除阻塞状态并且返回一个 nil 的多组合值(multi-bulk value)。

timeout 参数表示的是一个指定阻塞的最大秒数的整型值。当 timeout 为 0 是表示阻塞时间无限制。

什么 key 会先被处理?是什么客户端?什么元素?优先顺序细节。

  • 当客户端为多个 key 尝试阻塞的时候,若至少存在一个 key 拥有元素,那么返回的键值对(key/element pair)就是从左到右数第一个拥有一个或多个元素的key。 在这种情况下客户端不会被阻塞。比如对于这个例子 BLPOP key1 key2 key3 key4 0,假设 key2 和 key4 都非空, 那么就会返回 key2 里的一个元素。
  • 当多个客户端为同一个 key 阻塞的时候,第一个被处理的客户端是等待最长时间的那个(即第一个因为该key而阻塞的客户端)。 一旦一个客户端解除阻塞那么它就不会保持任何优先级,当它因为下一个 BLPOP 命令而再次被阻塞的时候,会在处理完那些 被同个 key 阻塞的客户端后才处理它(即从第一个被阻塞的处理到最后一个被阻塞的)。
  • 当一个客户端同时被多个 key 阻塞时,若多个 key 的元素同时可用(可能是因为事务或者某个Lua脚本向多个list添加元素), 那么客户端会解除阻塞,并使用第一个接收到 push 操作的 key(假设它拥有足够的元素为我们的客户端服务,因为有可能存在其他客户端同样是被这个key阻塞着)。 从根本上来说,在执行完每个命令之后,Redis 会把一个所有 key 都获得数据并且至少使一个客户端阻塞了的 list 运行一次。 这个 list 按照新数据的接收时间进行整理,即是从第一个接收数据的 key 到最后一个。在处理每个 key 的时候,只要这个 key 里有元素, Redis就会对所有等待这个key的客户端按照“先进先出”(FIFO)的顺序进行服务。若这个 key 是空的,或者没有客户端在等待这个 key, 那么将会去处理下一个从之前的命令或事务或脚本中获得新数据的 key,如此等等。

当多个元素被 push 进入一个 list 时 BLPOP 的行为

有时候一个 list 会在同一概念的命令的情况下接收到多个元素:

  • 像 LPUSH mylist a b c 这样的可变 push 操作。
  • 在对一个向同一个 list 进行多次 push 操作的 MULTI 块执行完 EXEC 语句后。
  • 使用 Redis 2.6 或者更新的版本执行一个 Lua 脚本。

当多个元素被 push 进入一个被客户端阻塞着的 list 的时候,Redis 2.4 和 Redis 2.6 或者更新的版本所采取行为是不一样的。

对于 Redis 2.6 来说,所采取的行为是先执行多个 push 命令,然后在执行了这个命令之后再去服务被阻塞的客户端。看看下面命令顺序。

Client A:   BLPOP foo 0
Client B:   LPUSH foo a b c

如果上面的情况是发生在 Redis 2.6 或更高版本的服务器上,客户端 A 会接收到 c 元素,因为在 LPUSH 命令执行后,list 包含了 c,b,a 这三个元素,所以从左边取一个元素就会返回 c。

相反,Redis 2.4 是以不同的方式工作的:客户端会在 push 操作的上下文中被服务,所以当 LPUSH foo a b c 开始往 list 中 push 第一个元素,它就被传送给客户端A,也就是客户端A会接收到 a(第一个被 push 的元素)。

Redis 2.4的这种行为会在复制或者持续把数据存入AOF文件的时候引发很多问题,所以为了防止这些问题,很多更一般性的、并且在语义上更简单的行为被引入到 Redis 2.6 中。

需要注意的是,一个Lua脚本或者一个 MULTI / EXEC 块可能会 push 一堆元素进入一个 list 后,再 删除这个 list。 在这种情况下,被阻塞的客户端完全不会被服务,并且只要在执行某个单一命令、事务或者脚本后 list 中没有出现元素,它就会被继续阻塞下去。

在一个 MULTI / EXEC 事务中的 BLPOP

BLPOP 可以用于流水线(pipeline,发送多个命令并且批量读取回复),特别是当它是流水线里的最后一个命令的时候,这种设定更加有意义。

在一个 MULTI / EXEC 块里面使用 BLPOP 并没有很大意义,因为它要求整个服务器被阻塞以保证块执行时的原子性,这就阻止了其他客户端执行一个 push 操作。 因此,一个在 MULTI / EXEC 里面的 BLPOP 命令会在 list 为空的时候返回一个 nil 值,这跟超时(timeout)的时候发生的一样。

如果你喜欢科幻小说,那么想象一下时间是以无限的速度在 MULTI / EXEC 块中流逝……

返回值

多批量回复(multi-bulk-reply): 具体来说:

  • 当没有元素的时候会弹出一个 nil 的多批量值,并且 timeout 过期。
  • 当有元素弹出时会返回一个双元素的多批量值,其中第一个元素是弹出元素的 key,第二个元素是 value。

例子

redis> DEL list1 list2
(integer) 0
redis> RPUSH list1 a b c
(integer) 3
redis> BLPOP list1 list2 0
1) "list1"
2) "a"

可靠的队列

BLPOP 返回一个元素给客户端的时候,它也从 list 中把该元素移除。这意味着该元素就只存在于客户端的上下文中:如果客户端在处理这个返回元素的过程崩溃了,那么这个元素就永远丢失了。

在一些我们希望是更可靠的消息传递系统中的应用上,这可能会导致一些问题。在这种时候,请查看 BRPOPLPUSH 命令,这是 BLPOP 的一个变形,它会在把返回元素传给客户端之前先把该元素加入到一个目标 list 中。

模式:事件提醒

用来阻塞 list 的操作有可能是不同的阻塞原语。 比如在某些应用里,你也许会为了等待新元素进入 Redis Set 而阻塞队列,直到有个新元素加入到 Set 中,这样就可以在不轮询的情况下获得元素。 这就要求要有一个 SPOP 的阻塞版本,而这事实上并不可用。但是我们可以通过阻塞 list 操作轻易完成这个任务。

消费者会做的:

LOOP forever
    WHILE SPOP(key) returns elements
        ... process elements ...
    END
    BRPOP helper_key
END

而在生产者这角度我们可以这样简单地使用:

MULTI
SADD key element
LPUSH helper_key x
EXEC
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 221,695评论 6 515
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,569评论 3 399
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 168,130评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,648评论 1 297
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,655评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,268评论 1 309
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,835评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,740评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,286评论 1 318
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,375评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,505评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,185评论 5 350
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,873评论 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,357评论 0 24
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,466评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,921评论 3 376
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,515评论 2 359

推荐阅读更多精彩内容

  • 原帖地址:https://www.jianshu.com/p/2f14bc570563 redis概述 Redis...
    onlyHalfSoul阅读 2,176评论 0 28
  • 欢迎大家关注我的其他 Github博客 和 Csdn ,互相交流! 1. Redis 简介 •Redis是一款开源...
    程序员祝融阅读 579评论 2 9
  • 1.1 资料 ,最好的入门小册子,可以先于一切文档之前看,免费。 作者Antirez的博客,Antirez维护的R...
    JefferyLcm阅读 17,067评论 1 51
  • 转载:Redis 宝典 | 基础、高级特性与性能调优 本文由 DevOpsDays 本文由简书作者kelgon供稿...
    meng_philip123阅读 3,130评论 1 34
  • Redis是啥 Redis是一个开源的key-value存储系统,由于拥有丰富的数据结构,又被其作者戏称为数据结构...
    一凡呀阅读 1,176评论 0 5