当前位置:首页 > 问答 > 正文

Redis里那些没被用掉的力量,消息堆着却没人去消费到底咋回事

这事儿说白了,就像是你在一个特别热闹的集市上开了个快递代收点(这个点就是Redis),你跟大家说:“都把快递放我这吧,我来帮大家存着,谁要来取自己拿,又快又方便!”一开始,大家都觉得这主意棒极了,发送快递的人(消息生产者)呼呼地往你这塞包裹,你的小仓库(Redis的内存)里瞬间堆满了各种大小的盒子(消息)。

问题就出在取快递的人(消息消费者)身上,可能出现了以下几种情况,导致包裹堆成了山,却没人来领:

第一种情况:取件码发丢了,或者取件人根本没看手机。 这对应到技术里,就是消息虽然成功存进了Redis的列表(List)或者更高级的流(Stream)这种数据结构里,但是消费它的那个程序(消费者)可能压根就没启动,或者启动后因为某种bug挂掉了,根本不知道有活要干,又或者,消费者程序是好的,但它去订阅(subscribe)的那个频道(channel)名字,和生产者在发布(publish)消息时用的频道名字对不上,就像一个快递员把包裹放到了A小区的1号柜,却通知了B小区的住户来取,那当然永远也取不着,消息就这么静静地躺在Redis里,直到天荒地老。

第二种情况:取件人来了,但力气太小,搬不动大箱子。 这就是典型的消费者处理能力不足,生产者是个“剁手狂魔”,一秒钟能下十个单,生成十条消息塞进Redis,而消费者呢,可能因为业务逻辑复杂,或者连接数据库慢,处理一条消息就得花两秒钟,这样一来,生产速度远远大于消费速度,消息自然就会越积越多,Redis虽然读写得快,但它只是个临时中转站,不是永久仓库,内存是有限的,当消息多到把内存撑爆时,Redis要么会按照你设定的规则淘汰掉一些旧消息(如果设置了的话),要么就直接报错,新消息也写不进去了,整个服务可能就卡壳了。

第三种情况:取件流程太复杂,一个人一次只能拿一个,后面排长队。 有些时候,消费者程序可能是单线程处理的,或者虽然开了多个线程/进程,但它们需要顺序地处理同一种类型的消息,不能搞乱顺序,这就好比只有一个快递员在负责从货架上拿包裹,哪怕货架上有成百上千个包裹,他也只能一个一个处理,这种情况下,即使单个消息处理得不慢,但面对海量消息时,吞吐量也上不去,队列还是会越来越长。

第四种情况:取件人拿是拿了,但刚出门口就被绊了一跤,包裹摔坏了,他也没说。 这在技术上称为“消息丢失”,有些消费者程序在从Redis里取出(pop)一条消息后,会先开始处理,如果处理到一半程序崩溃了,这条消息就已经从Redis里消失了,没有别的备份,这个消息代表的这个任务就永远丢失了,相当于没人处理,虽然Redis本身很可靠,但这种消费模式存在风险,为此,Redis提供了更可靠的Stream数据类型,它支持“消费者组”(Consumer Group)和“消息确认”(ACK)机制,简单说,就是消息被消费者取走处理后,消费者必须明确地告诉Redis:“嘿,这条消息我搞定啦,你可以把它删了。”如果消费者处理失败没来得及确认,过一段时间这条消息又会重新出现在队列里,让其他健康的消费者去处理,但如果开发者没用好这个确认机制,或者消费者一直不确认,也可能导致消息处于“正在处理中”的悬空状态,看似被消费了,实则没有结果。

第五种情况:集市管理方(也就是开发者自己)的疏忽。 可能是一些临时的、测试用的消息被生产出来,后来忘了关掉生产程序,或者某个业务功能下线了,但对应的消息生产代码没停,这就好比一个商家不停地往你的代收点寄送试用品,但根本没人想要这些东西,它们就白白占满了你的仓库空间。

回到你的问题“Redis里那些没被用掉的力量,消息堆着却没人去消费到底咋回事?”——核心原因通常不在Redis本身,它只是个尽职尽责的“快递柜”,问题往往出在围绕Redis构建的这套消息流程上:可能是消费者不见了、消费者太慢、消费方式不对、或者流程设计有漏洞,要解决这个问题,就得像排查快递堵塞一样,去检查:消费者程序还活着吗?生产速度和消费速度匹配吗?消费逻辑能优化吗?是否需要用上更可靠的Stream和确认机制?有没有一些无效的消息来源需要被掐掉?把这些环节理顺了,Redis这个“超级快递柜”的力量才能真正被高效、可靠地利用起来。


Redis里那些没被用掉的力量,消息堆着却没人去消费到底咋回事