后端工程师的高阶面经
邓明
前 Shopee 高级工程师,Beego PMC
6888 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 50 讲
后端工程师的高阶面经
15
15
1.0x
00:00/00:00
登录|注册

27|重复消费:高并发场景下怎么保证消息不会重复消费?

你好,我是大明。今天我们来讨论一个在消息队列里面非常常见的话题——重复消费。
通过前面几节课的学习,我相信你已经发现了,很多方案都会引起一个问题:消息重复发送或者重复消费。而解决的思路基本上一致,就是把消费者设计成幂等的。也就是说,同一个消息,不管消费多少次,系统状态都是一样的。
另外一个经常和幂等联系在一起的话题就是重试。就像你在微服务部分接触到的那样,为了提高可用性,我们经常会引入各种重试机制。而重试的一个前提就是重试的动作必须是幂等的。所以,在面试中幂等是一个绕不开的话题。只不过大部分人在讨论幂等的时候,只能想到使用唯一索引,而且即便回答唯一索引,也很难深入。
所以今天我就带你从重复消费讨论到幂等,最后给出一个非常强大的高并发幂等方案,确保你在面试的时候可以赢得竞争优势。

布隆过滤器

布隆过滤器(Bloom Filter)是一种数据结构,它可以用于检索一个元素是否在一个集合里。它的优点是空间效率和查询时间都远远超过一般的算法,缺点是存在假阳性的问题,还有对于删除操作不是很友好。
布隆过滤器的基本思路是当集合加入某个元素的时候,通过哈希算法把元素映射到比特数组的 N 个点,把对应的比特位设置成 1。
在查找的时候,只需要看对应的比特位是不是 1,就可以粗略判断集合里有没有这个元素。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文深入探讨了在高并发场景下如何保证消息不会重复消费的问题,并提出了多种解决方案。首先介绍了布隆过滤器的基本原理和优缺点,然后讨论了重复消费的可能原因,包括生产者重复发送和消费者重复消费。作者强调了将消费逻辑设计成幂等的重要性,并提出了在面试前需要了解的问题。此外,文章还介绍了基于本地布隆过滤器、Redis和唯一索引的高级方案,以确保到达数据库的流量最小化。同时,还提出了使用布隆过滤器的替代品——bit array,以提高性能并节省内存。总的来说,本文内容丰富,涵盖了技术细节和实际应用,对于需要解决高并发场景下消息队列相关问题的读者具有一定的借鉴意义。文章还提出了思考题,引发读者对于不同隔离级别下的操作阻塞和方案效果的思考,为读者提供了进一步思考和讨论的空间。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《后端工程师的高阶面经》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(17)

  • 最新
  • 精选
  • shuff1e
    之前遇到过类似场景,QPS不高,用了一个分布式的悲观锁,同一个key的请求都会竞争这个分布式锁,只有竞争成功的才会继续往下执行。 没有想到过布隆过滤机,bit array,再加上redis的方案,可能是场景也不太一样。 不过也不得不感叹一句,人外有人,天外有天,大明老师还是对这种case做了一个系统的分析。

    作者回复: 嘿嘿,感谢感谢。你还可以考虑将同一个 Key 的都打过去一个分区,然后直接就不需要锁了。小心 rebalance 问题就可以。

    2023-08-18归属地:北京
    4
  • humor
    布隆过滤器 + 唯一索引的方案,不管走不走布隆过滤器,都会校验是否发生唯一冲突,那么布隆过滤器的意义是什么呢?因为每个消息都还会校验一下数据库的唯一性,有没有布隆过滤器对数据库来说都一样啊

    作者回复: 好处就是挡住绝大部分重复请求。也就是说,如果你基本没有重复请求,那么这个方案就没啥用。

    2023-08-18归属地:浙江
    8
    4
  • 江 Nina
    感觉布隆过滤器的假阳性坑挺大的,面试的时候能否这么设计呢:bitmap数组 + redis + mysql唯一索引,作为两点方案呢,因为大多数业务的用的唯一约束都是数字感觉。

    作者回复: 可以啊,这就是我在后面提到的,你可以用 bitmap 取代布隆过滤器。 不过bitmap 之后其实没太大必要接 Redis 了,因为 bitmap 咩有假阳性。也就是说,Bitmap 说有就是有,没有就是没有。

    2023-08-30归属地:北京
    2
    2
  • 天天有吃的
    老师请问下,redis中的布隆过滤器长度是默认的,还是自己设置的呢?如果是自己设置的,多少长度合适呢?我想有没有一种情况位数组所有的元素都是1,那么布隆过滤器不是没用了?redis中会自动扩容吗?

    作者回复: 布隆过滤器是全量的,所以你有多少数据,就有多长,甚至还要长一些,因为要考虑冲突的问题(假阳性)。

    2024-03-04归属地:福建
    1
  • Lum
    Read Committed 隔离级别下:如果两个事务同时插入相同的数据,那么后提交的事务会被阻塞,等待先提交的事务完成后才能继续执行。这是因为 Read Committed 隔离级别只保证了读取到的数据是已经提交的,但是未提交的数据仍然可以被其他事务读取到。 Repeatable Read 隔离级别下:如果两个事务同时插入相同的数据,那么后提交的事务会被回滚,因为 Repeatable Read 隔离级别保证了事务在执行过程中多次读取同一个数据时,得到的结果是一致的。如果有重复的插入操作,则会破坏这个一致性,因此后提交的事务会被回滚。

    作者回复: 赞! 这边你说“但是未提交的数据仍然可以被其他事务读取到”是不是写错了?这个应该是 Read Uncommitted 吧?

    2023-10-26归属地:江苏
    1
  • Geek_48fcdf
    布隆+redis+唯一键方案里,布隆过滤器如果使用机器内存则那么无法解决分布式问题,如果使用redis存就得查两次redis,这里的布隆过滤器怎么个部署才有意义?

    作者回复: 一个是结合前面的一致性哈希负载均衡,这样使用本地的布隆过滤器效果还可以。 如果使用 Redis 的话,确实是难以避免两次 Redis 操作。也可以考虑使用 lua 脚本直接封装。

    2023-09-08归属地:北京
    1
  • 陈斌
    问题1: 如果隔离级别不是RR,就会出现在插入成功唯一索引之后,业务操作完成之后提交事务可能会出现失败的情况,导致因为索引冲突而引起的不必要的回滚。如果隔离级别为RR的话,就不会出现上述情况。 问题2: 老师您说的最后一种方案我认为是:布隆过滤器 + redis + 唯一索引方案 重复请求占比为百分之1的话,该方案可以将近99%的流量挡在布隆过滤器 与 redis 层级,当然前提是redis的键值失效时间设置合理或者说重复请求的间隔时间很短或者说布隆过滤器没有出现假阳性,此时系统可以承受高并发流量。

    作者回复: 哈哈哈哈,问题 2 可能出乎你的预料,重复请求占比 1% 的话,你只能挡住这 1% 的重复请求。 你可能会奇怪,这不就是寄了吗?但是你要知道,如果你连正常的流量都处理不了,就不用谈别的了。

    2023-08-19归属地:广东
    4
    1
  • ilake
    > 关键点是在 RR 隔离级别下重复请求的插入操作会被阻塞。那么如果隔离级别不是 RR 的话,你觉得会发生什么? - 在 Repeatable Read(RR)隔离级别下,事务会在一开始就发现阻塞,因为在事务开始时,就会锁定读取的数据,确保了事务期间不会有其他事务修改这些数据。 - 在 Read Committed(RC)隔离级别下,事务是在稍后才发现阻塞的,因为它们在读取数据时不会锁定行,只有在稍后尝试修改数据时,如果发现有其他事务已经修改了相同的数据,才会发生阻塞 同樣都會被阻塞,只是時間點不同。 請問這樣理解正確嗎?

    作者回复: 我个人认为这种说法也没问题。并且,这两者加的锁也是有差别的。

    2023-12-01归属地:日本
  • Geek8004
    布隆过滤器+redis+mysql 解决幂等消费问题的时候,假如重复的消息百分之2是不是就没多大用处? 重复消息到达百分是多少才能用这个方案.大概是什么样的场景配置可以用到这个场景;

    作者回复: 对,重复请求不多效果就不好。至于比例多少,你可以自己根据业务来评估。我举个例子,如果你的数据库负载不高,那么你都不需要这个方案。如果你的数据库负载本身就很高了,那么你就算有一点点重复流量,也可以考虑加上去。

    2023-11-10归属地:广东
  • 一弦一柱思华年
    老师您好,请问如果用bit array,加上一致性哈希负载均衡,是不是可以不用DB兜底了呢,因为不存在假阳性问题;另外,这个布隆过滤器是不是需要定期清空啊,如果时间久了,布隆过滤器里的每一位都设置为了1,那得到的结果就永远是存在了,此时布隆过滤器就失效了。还有一个疑惑点:布隆过滤器+唯一索引的方案,布隆过滤器判断不存在的话,是不是也需要把key插入到DB,这样的话,判断存在和不存在都涉及DB更新,削流作用是怎么体现的呢

    作者回复: 1. 使用 DB 兜底还可以有效防止节点崩溃,或者 Redis 崩溃的问题 2. 一般设计布隆过滤器的时候,都会默认这个布隆过滤器能装下所有的数据,毕竟布隆过滤器本身消耗资源不多,所以至少我是没删除过布隆过滤器。不过这算是一个很好的思路,学习了。 3. 主要是体现在重复请求的时候,可以避免 DB 操作。所以我说这个方案如果重复请求比例不高的时候,其实也没那么好用。

    2023-11-07归属地:广东
收起评论
显示
设置
留言
17
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部