Redis核心技术与实战
蒋德钧
中科院计算所副研究员
新⼈⾸单¥19.9
7331 人已学习
课程目录
已更新 20 讲 / 共 50 讲
0/4登录后,你可以任选4讲全文学习。
开篇词 (1讲)
开篇词 | 这样学Redis,才能技高一筹
免费
基础篇 (10讲)
01 | 基本架构:一个键值数据库包含什么?
02 | 数据结构:快速的Redis有哪些慢操作?
03 | 高性能IO模型:为什么单线程Redis能那么快?
04 | AOF日志:宕机了,Redis如何避免数据丢失?
05 | 内存快照:宕机后,Redis如何实现快速恢复?
06 | 数据同步:主从库如何实现数据一致?
07 | 哨兵机制:主库挂了,如何不间断服务?
08 | 哨兵集群:哨兵挂了,主从库还能切换吗?
09 | 切片集群:数据增多了,是该加内存还是加实例?
10 | 第1~9讲课后思考题答案及常见问题答疑
实践篇 (7讲)
11 | “万金油”的String,为什么不好用了?
12 | 有一亿个keys要统计,应该用哪种集合?
13 | GEO是什么?还可以定义新的数据类型吗?
14 | 如何在Redis中保存时间序列数据?
15 | 消息队列的考验:Redis有哪些解决方案?
16 | 异步机制:如何避免单线程模型的阻塞?
17 | 为什么CPU结构也会影响Redis的性能?
加餐篇 (2讲)
加餐(一)| 经典的Redis学习资料有哪些?
加餐(二)| Kaito:我是如何学习Redis的?
Redis核心技术与实战
15
15
1.0x
00:00/00:00
登录|注册

15 | 消息队列的考验:Redis有哪些解决方案?

蒋德钧 2020-09-11
你好,我是蒋德钧。
现在的互联网应用基本上都是采用分布式系统架构进行设计的,而很多分布式系统必备的一个基础软件就是消息队列。
消息队列要能支持组件通信消息的快速读写,而 Redis 本身支持数据的高速访问,正好可以满足消息队列的读写性能需求。不过,除了性能,消息队列还有其他的要求,所以,很多人都很关心一个问题:“Redis 适合做消息队列吗?”
其实,这个问题的背后,隐含着两方面的核心问题:
消息队列的消息存取需求是什么?
Redis 如何实现消息队列的需求?
这节课,我们就来聊一聊消息队列的特征和 Redis 提供的消息队列方案。只有把这两方面的知识和实践经验串连起来,才能彻底理解基于 Redis 实现消息队列的技术实践。以后当你需要为分布式系统组件做消息队列选型时,就可以根据组件通信量和消息通信速度的要求,选择出适合的 Redis 消息队列方案了。
我们先来看下第一个问题:消息队列的消息读取有什么样的需求?

消息队列的消息存取需求

我先介绍一下消息队列存取消息的过程。在分布式系统中,当两个组件要基于消息队列进行通信时,一个组件会把要处理的数据以消息的形式传递给消息队列,然后,这个组件就可以继续执行其他操作了;远端的另一个组件从消息队列中把消息读取出来,再在本地进行处理。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《Redis核心技术与实战》,如需阅读全部文章,
请订阅文章所属专栏新⼈⾸单¥19.9
立即订阅
登录 后留言

精选留言(11)

  • Kaito
    如果一个生产者发送给消息队列的消息,需要被多个消费者进行读取和处理,你会使用Redis的什么数据类型来解决这个问题?

    这种情况下,只能使用Streams数据类型来解决。使用Streams数据类型,创建多个消费者组,就可以实现同时消费生产者的数据。每个消费者组内可以再挂多个消费者分担读取消息进行消费,消费完成后,各自向Redis发送XACK,标记自己的消费组已经消费到了哪个位置,而且消费组之间互不影响。

    另外,老师在介绍使用List用作队列时,为了保证消息可靠性,使用BRPOPLPUSH命令把消息取出的同时,还把消息插入到备份队列中,从而防止消费者故障导致消息丢失。

    这种情况下,还需要额外做一些工作,也就是维护这个备份队列:每次执行BRPOPLPUSH命令后,因为都会把消息插入一份到备份队列中,所以当消费者成功消费取出的消息后,最好把备份队列中的消息删除,防止备份队列存储过多无用的数据,导致内存浪费。

    这篇文章主要是讲消息队列的使用,借这个机会,也顺便总结一下使用消息队列时的注意点:

    在使用消息队列时,重点需要关注的是如何保证不丢消息?

    那么下面就来分析一下,哪些情况下,会丢消息,以及如何解决?

    1、生产者在发布消息时异常:

    a) 网络故障或其他问题导致发布失败(直接返回错误,消息根本没发出去)
    b) 网络抖动导致发布超时(可能发送数据包成功,但读取响应结果超时了,不知道结果如何)

    情况a还好,消息根本没发出去,那么重新发一次就好了。但是情况b没办法知道到底有没有发布成功,所以也只能再发一次。所以这两种情况,生产者都需要重新发布消息,直到成功为止(一般设定一个最大重试次数,超过最大次数依旧失败的需要报警处理)。这就会导致消费者可能会收到重复消息的问题,所以消费者需要保证在收到重复消息时,依旧能保证业务的正确性(设计幂等逻辑),一般需要根据具体业务来做,例如使用消息的唯一ID,或者版本号配合业务逻辑来处理。

    2、消费者在处理消息时异常:

    也就是消费者把消息拿出来了,但是还没处理完,消费者就挂了。这种情况,需要消费者恢复时,依旧能处理之前没有消费成功的消息。使用List当作队列时,也就是利用老师文章所讲的备份队列来保证,代价是增加了维护这个备份队列的成本。而Streams则是采用ack的方式,消费成功后告知中间件,这种方式处理起来更优雅,成熟的队列中间件例如RabbitMQ、Kafka都是采用这种方式来保证消费者不丢消息的。

    3、消息队列中间件丢失消息

    上面2个层面都比较好处理,只要客户端和服务端配合好,就能保证生产者和消费者都不丢消息。但是,如果消息队列中间件本身就不可靠,也有可能会丢失消息,毕竟生产者和消费这都依赖它,如果它不可靠,那么生产者和消费者无论怎么做,都无法保证数据不丢失。

    a) 在用Redis当作队列或存储数据时,是有可能丢失数据的:一个场景是,如果打开AOF并且是每秒写盘,因为这个写盘过程是异步的,Redis宕机时会丢失1秒的数据。而如果AOF改为同步写盘,那么写入性能会下降。另一个场景是,如果采用主从集群,如果写入量比较大,从库同步存在延迟,此时进行主从切换,也存在丢失数据的可能(从库还未同步完成主库发来的数据就被提成主库)。总的来说,Redis不保证严格的数据完整性和主从切换时的一致性。我们在使用Redis时需要注意。

    b) 而采用RabbitMQ和Kafka这些专业的队列中间件时,就没有这个问题了。这些组件一般是部署一个集群,生产者在发布消息时,队列中间件一般会采用写多个节点+预写磁盘的方式保证消息的完整性,即便其中一个节点挂了,也能保证集群的数据不丢失。当然,为了做到这些,方案肯定比Redis设计的要复杂(毕竟是专们针对队列场景设计的)。

    综上,Redis可以用作队列,而且性能很高,部署维护也很轻量,但缺点是无法严格保数据的完整性(个人认为这就是业界有争议要不要使用Redis当作队列的地方)。而使用专业的队列中间件,可以严格保证数据的完整性,但缺点是,部署维护成本高,用起来比较重。

    所以我们需要根据具体情况进行选择,如果对于丢数据不敏感的业务,例如发短信、发通知的场景,可以采用Redis作队列。如果是金融相关的业务场景,例如交易、支付这类,建议还是使用专业的队列中间件。
    2020-09-11
    9
    43
  • pedro
    每次想回答问题,看到 Kaito 的留言,顿时就有一种“眼前有景道不得,崔颢题诗在上头”的感觉。
    2020-09-11
    2
    7
  • 杨逸林
    1. 问题回答
    每个 Stream 都可以挂多个消费组,每个消费组会有个游标 last_delivered_id 在 Stream 数组之上往前移动,表示当前消费组已经消费到哪条消息了。如果这是对的,那只需要开两个消费者组,消费者 1 在组1,消费者2在组2不就行了么。
    这个 last_delivered_id 有点像 Kafka 的 offset。

    2. 有点不同的观点
    Kafka 其实已经考虑去掉 ZK 了,而且适合高吞吐量的业务,不过不适合流式处理。专业的事,还是教给专业的来做好点。

    我之前用过 Redis 的 publish 和 subscribe 命令来做不同系统的数据同步。不过那个在用 Jedis 来订阅有坑,必须循环调用,不然会在一定时间后被 down 掉。
    2020-09-11
    3
  • 小喵喵
    请教下老师两个问题:
    1.如果消息延迟了,如何做好监控?
    2.如何尽可能的减少消息的延迟,我好像知道一个很牛逼的技术叫“零拷贝”,但是不太理解其原理,望老师解惑。谢谢。
    2020-09-11
    1
    1
  • 刀斧手何在
    Redis做消息队列的优势是高并发写入性能和水平扩展能力。最大的坑 我觉得就是Kaito同学说的第3点 数据可靠性
    2020-09-11
    1
  • 豪森布鲁斯王
    老师您好,我们现在遇到一个异步抢占式的需求,多个生产者产生大量的数据,放入缓存中,然后多个消费者从缓存中获取,要保证每个数据只能被一个消费者消费,生产者可能产生大量的任务,至少千万量级吧,这种场景适合使用redis吗,应该使用哪种模式呢?
    2020-09-14
  • zhou
    BRPOP 命令阻塞功能依赖客户端实现吧,如果客户端没有实现阻塞,这个命令是不是就没阻塞效果
    2020-09-14
  • 一步
    xread 读取消息的时候,设置 block 1000 ,当 stream 没有消息的时候会阻塞 1000 ms, 那么当 stream 有新的消息过来后,会重新计算阻塞的时间吗?
    2020-09-12
  • 土豆白菜
    茅草房-土房-平房
    2020-09-11
  • DKSky
    老师的文中说“我们希望启动多个消费者程序组成一个消费组,一起分担处理 List 中的消息。但是,List 类型并不支持消费组的实现。那么,还有没有更合适的解决方案呢?“
    这句话不太明白,如果多个消费者客户端使用brpop命令阻塞式的“抢”列表尾部的元素,这些消费者不属于同一个consumer group吗?
    2020-09-11
  • MClink
    发布/订阅"(publish/subscribe)模式,支持重复消费
    2020-09-11
收起评论
11
返回
顶部