关于 Redis 队列的一道面试题 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
tanteng
V2EX    Redis

关于 Redis 队列的一道面试题

a href="Javascript:" Onclick="upVoteTopic(261936);" class="vote">
  •  
  •   tanteng
    tanteng 2016-03-08 15:01:55 +08:00 30893 次点击
    这是一个创建于 3516 天前的主题,其中的信息可能已经有所发展或是发生改变。

    面试的时候讲到自己做的一个项目,我说用了 Redis 队列, lpush 进, rpop 出,面试官打断问:一般入队列没有问题,如果出对列阻塞了怎么办?实际生产环境中在这里没有出现问题,而且队列量很大,我没有太理解面试官问的什么。

    Redis 作队列需要注意什么问题?如果是高并发情况下怎么办?谢谢大家指点!

    第 1 条附言    2016-03-08 15:54:51 +08:00
    还问有没有考虑队列的长度呢,我在实际开发中,没有考虑过这个问题, redis 可以一直入队列吧,不过如果队列太大,有可能占满内存,那这种情况怎么办?
    38 条回复    2018-03-17 13:20:35 +08:00
    tanteng
        1
    tanteng  
    OP
       2016-03-08 15:11:08 +08:00
    还有另外的地方用的是 HTTPSQS 消息队列系统,还有 Kafka
    CosWind
        2
    CosWind  
       2016-03-08 15:38:13 +08:00
    同没有 get 到面试官的点,队列阻塞也分很多种情况,和业务挂钩;
    如果是单纯的处理能力不够,增加 consumer 就可以;
    如果对顺序有要求,要对消息作 shard 。。

    但这么问,实在 get 不到点呀
    gaocheng
        3
    gaocheng  
       2016-03-08 15:41:23 +08:00
    1 、队列阻塞问题做好报警
    2 、从业务方面考虑能够动态调控提高队列处理能力
    sampeng
        4
    sampeng  
       2016-03-08 15:43:09 +08:00
    面试官的意思就是字面意思啊。。堵塞。。消费者挂掉 or 太慢。怎么处理
    sampeng
        5
    sampeng  
       2016-03-08 15:44:26 +08:00   1
    单纯加 cunsumer 真可以么。。资源不是无限的。
    我这线上就有一个典型例子,消费者突然处理变慢,加到能加的极限了,然后,然后就 redis 的内存吃满领便当了。
    tanteng
        6
    tanteng  
    OP
       2016-03-08 15:59:16 +08:00
    @sampeng 那如何解决的呢
    Mirana
        7
    Mirana  
       2016-03-08 16:17:04 +08:00
    我怎么觉得阻塞的意思是队列空了呢
    iyaozhen
        8
    iyaozhen  
       2016-03-08 16:17:17 +08:00   2
    最近就遇到这个问题了。

    消费者的速度没有生产者快,增加消费者也缓解不了, Redis 会被打满,直到挂掉(影响其它业务功能)。而且 Redis 目前的数据清理策略是根据 key 的,如果设置了清理用作队列的那个 list key 会被清掉,如果这是业务代码写的不是太好的话消费者和生产者都会挂掉。消费者最好还是异步消费,不要阻塞队列。

    当然还是要对队列长度做监控。

    目前已经转投 Kafka 了。
    iyaozhen
        9
    iyaozhen  
       2016-03-08 16:18:50 +08:00
    @Mirana 空了没关系吧, sleep 几秒。或者使用 brpop 指令。
    tanteng
        10
    tanteng  
    OP
       2016-03-08 16:19:55 +08:00
    @iyaozhen 消费者异步消费?这里能具体讲讲吗
    sampeng
        11
    sampeng  
       2016-03-08 16:23:39 +08:00   1
    kafka 是正理。。
    redis 只适合快速消费不会堆积的业务场景上,数据存内存里实在太贵了。。
    lecher
        12
    lecher  
       2016-03-08 16:24:10 +08:00 via Android   3
    出队列阻塞就是消费者的处理速度跟不上生产者产生的任务,加消费者提高处理速度。
    消费者加到极限还处理不过来,实在有爆内存的风险的时候,可以考虑将任务数据持久化,避免数据丢失,先把任务状态保存下来,根据具体业务做优化调整,等待异常问题解决之后再继续处理。
    Mirana
        13
    Mirana  
      &nbp;2016-03-08 16:26:28 +08:00   1
    消费和生产有数量级的差异的话,增加消费者也是没用的,数据重要就放到数据库里,不重要就直接丢掉。
    tabris17
        14
    tabris17  
       2016-03-08 16:26:29 +08:00   1
    使用支持持久化的队列消息,比如 rabbitmq
    iyaozhen
        15
    iyaozhen  
       2016-03-08 16:26:35 +08:00
    @tanteng 额,可能说的不是太专业。看具体业务吧,你拿到数据肯定是需要做一些处理,这个过程可以异步操作,比如异步写文件,异步入库等。甚至继续向下游业务抛,压力往后转移。
    tanteng
        16
    tanteng  
    OP
       2016-03-08 16:35:10 +08:00
    @CosWind
    @sampeng
    增加消费者会不会存在并发取 redis 队列的情况,这种情况要如何处理
    cYcoco
        17
    cYcoco  
       2016-03-08 16:37:11 +08:00
    用 kafka 好了吧。在我印象里 redis 更适合做类 memcached 这种内存缓存吧。不过对 redis 不太了解。。
    lijinma
        18
    lijinma  
       2016-03-08 17:12:03 +08:00   1
    关于你的问题我遇见过:
    1. Redis 关于内存满了如何处理的策略要配置好,是使用 lru 还是 noviction 等,一定要想清楚, Redis 可使用的内存大小要配置适当,最好是你 Redis 内存使用峰值的几倍甚至 10 倍。

    2. Redis 内存要做好报警,简单的做法是每分钟或更短的时间使用 info 指令获取内存使用情况,如果超过某个值要马上想办法处理。

    3. 如果能多加 consumer ,就尝试多加 consumer 解决。

    4. 如果还没有解决,你可能需要想办法提升 Redis 内存占用,可能想办法平行扩展 Redis Server 。

    5. 如果还没有解决,就只能想办法用 kafka 这种可以持久化的方式了,或者你自己设计一种 Redis 的持久化方式,因为内存总归是有限的且稀缺的。
    tanteng
        19
    tanteng  
    OP
       2016-03-08 17:23:52 +08:00
    稍微总结一下:对于大数据的队列不适合用 Redis 弄,考虑 HTTPSQS , RabbitMQ 之类的消息队列系统,或者 Kafka , Redis 队列适合小型,快速,消费也快的场景。
    neoblackcap
        20
    neoblackcap  
       2016-03-08 17:32:33 +08:00 via iPhone
    @tanteng 不会, redis 就是一单线程实现,所有操作都是顺序执行
    Lucups
        21
    Lucups  
       2016-03-08 17:35:22 +08:00   1
    首先, Redis 是支持持久化的,所以队列数据完全可以持久化;其次, Consumer(Worker) 消化不了扛不住,先尝试多开一些,如果还不行,说明硬件或其他地方(如数据库存储)存在瓶颈,跟队列本身有毛关系?真以为上 Kafka 就能解决问题了?感觉题主没有 Get 到要点。
    keakon
        22
    keakon  
       2016-03-08 17:37:50 +08:00
    其实是让你用 epoll 之类的注册事件,等到可读时再去读…
    hxsf
        23
    hxsf  
       2016-03-08 17:48:45 +08:00
    @tanteng redis 单线程,不存在并发读写,总会有个先来后到的。
    hxsf
        24
    hxsf  
       2016-03-08 17:54:32 +08:00
    @keakon 直接 BRPOP 不就好了,读不到就阻塞,直到读到了再返回。
    wikimore
        25
    wikimore  
       2016-03-08 18:00:27 +08:00
    BRPOP 一个时间周期(如阻塞 30 秒)轮询,不能一直阻塞,因为长时间连接没有读写可能会被系统干掉,作为客户端就永远阻塞在那里了,而同时服务端的队列里可能有很多消息了。
    500miles
        26
    500miles  
       2016-03-08 18:00:44 +08:00   2
    按我的理解, 是说 " consumer 吞吐量太弱, 不及 producer 生产速度, 此时何解? "

    这就难玩儿了. . 就好比要解决北上广住房问题 .


    不考虑移民 (投靠其他技术选型),

    1. 监控队列, 动态调配 consumer, 适时增加 consumer

    最简单有效的办法, 从根本上解决问题. 当然你得花钱加机器

    2. 做好降级方案, 达到预设阀值, 出队后暂不处理, 直接先持久化再说


    3. consumer 投递异步任务, 不要阻塞出队列

    这个比较难玩儿
    wikimore
        27
    wikimore  
       2016-03-08 18:07:37 +08:00   1
    redis 做队列主要问题应该就是内存,如果使用到 swap 的话, redis 的队列会异常的慢,还有就是消息的大小,不要超过 1K ,再大写入基本就是很慢了,还有就是主从保证高可用,如果量非常大,可以多实例做 partition ,不过这时候用 Kafka 明显更划算, Redis 做做小业务玩玩还是可以的。
    bengol
        28
    bengol  
       2016-03-08 18:12:11 +08:00
    允许请求超时 /失败,做好失败后处理
    imlewc
        29
    imlewc  
       2016-03-08 18:28:01 +08:00
    分布式 一键解决
    keakon
        30
    keakon  
       2016-03-08 19:00:38 +08:00
    @hxsf 你的服务有 10 万并发时,第一个堵塞了,后面的都傻眼?
    isno
        31
    isno  
       2016-03-08 19:24:23 +08:00 via iPhone
    涉及队列的任务学会先消费后处理
    CosWind
        32
    CosWind  
       2016-03-08 19:45:45 +08:00
    @tanteng 还是不要用 redis 吧,我们用的是 rabbitmq
    22too
        33
    22too  
       2016-03-08 19:52:57 +08:00
    我们也是 rabbitmq
    hxsf
        34
    hxsf  
       2016-03-08 20:42:46 +08:00   1
    @keakon 读不到才阻塞(阻塞消费者),后面的消费者也会一起排队,有内容的时候按先后顺序解除阻塞啊。
    redis 文档上的啊,做队列用的时候建议使用 R

    官网文档 http://redis.io/commands/brpop

    以下引用他人翻译的

    列表的阻塞操作 (blocking)
    列表有一个特别的特性使得其适合实现队列,通常作为进程间通信系统的积木:阻塞操作。

    假设你想往一个进程的列表中添加项,用另一个进程来处理这些项。这就是通常的生产者消费者模式,可以使用以下简单方式实现:

    生产者调用 LPUSH 添加项到列表中。
    消费者调用 RPOP 从列表提取 / 处理项。
    然而有时候列表是空的,没有需要处理的, RPOP 就返回 NULL 。所以消费者被强制等待一段时间并重试 RPOP 命令。这称为轮询(polling),由于其具有一些缺点,所以不合适在这种情况下:

    强制 Redis 和客户端处理无用的命令 (当列表为空时的所有请求都没有执行实际的工作,只会返回 NULL)。
    由于工作者受到一个 NULL 后会等待一段时间,这会延迟对项的处理。
    于是 Redis 实现了 BRPOP 和 BLPOP 两个命令,它们是当列表为空时 RPOP 和 LPOP 的会阻塞版本:仅当一个新元素被添加到列表时,或者到达了用户的指定超时时间,才返回给调用者。
    moe3000
        35
    moe3000  
       2016-03-08 21:04:38 +08:00
    1 、 brpop 可以解决
    2 、 redis 分布式部署?具体没研究过。。
    keakon
        36
    keakon  
       2016-03-09 02:22:12 +08:00
    @hxsf 我觉得我已经说得够清楚了,你实际去做的话,就知道面试官为什么会这样问了…
    solaro
        37
    solaro  
       2016-03-09 11:12:14 +08:00
    @wikimore 咦,才 1K ,我之前做的都是限制 10K 以内, 5000/S 的吞吐量还是很快的。大促高峰期内存各种报警
    jinya
        38
    jinya  
       2018-03-17 13:20:35 +08:00
    马克
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     4874 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 29ms UTC 09:41 PVG 17:41 LAX 02:41 JFK 05:41
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86