付同学的技术客栈/

消息队列不是银弹,它只是把压力换了一个地方

所属专题 高并发海量数据系统架构 第 5 篇 / 共 11 篇 查看专题目录

活动开始后,下单接口最先扛不住。

用户点击下单,接口里要校验活动、检查库存、创建订单、扣优惠券、发通知、更新报表。平时这条链路没问题,活动流量一上来,接口开始超时。

这时候很多人会说:加 MQ。

这个判断不一定错,但它只说了一半。MQ 可以把同步链路拆开,可以把突发流量先接住,但它不会让压力消失。它只是把压力从接口线程、数据库事务和同步调用里,转移到了队列、消费者和补偿逻辑里。

如果后面消费能力跟不上,问题不会消失,只会换个名字:消息积压。

消息队列最容易被说成一句话:削峰填谷。

但真正上了项目以后,你会发现它没有这么轻松。流量是削下来了,可积压从哪里看,失败怎么重试,重复消费怎么处理,消费者挂了以后谁来补,都是要落到具体代码和运维动作里的。

我以前也见过一种情况:接口很快返回成功,业务方以为事情已经完成了。实际上消息还在队列里排着,后面的消费者已经慢了十几分钟。

这时候队列不是解决了问题,而是把问题换了一个地方继续等你。

现场判断 MQ 不是把压力变没,而是把压力从同步链路搬到了异步链路。

接口变快只是第一步,后面的积压、重试、幂等、补偿,才是项目里真正磨人的地方。

一、削峰是把流量摊平,不是把流量变没

活动高峰最难的是瞬时流量。

一秒内涌进来的请求,数据库和下游服务不一定能即时处理。MQ 的价值,是先把请求接住,再让消费者按系统能承受的速度慢慢处理。

突发请求
  -> 写入 MQ
  -> 消费者按能力消费
  -> 数据库稳定写入

这叫削峰填谷。

但这里有个前提:业务允许异步。

用户提交后能不能看到“处理中”?库存有没有被预占?支付超时后怎么释放?失败后怎么通知?这些问题没想清楚,MQ 只会把同步失败变成异步混乱。

异步链路 MQ 接住流量以后,后半段链路也要有人负责
生产者 业务提交成功后,消息要可靠发出去。
队列 削峰、持久化、积压监控,不能只当中转站。
消费者 慢消费、重复消费、异常重试都要按默认情况处理。
补偿 失败消息、死信队列、人工兜底,要能把业务收回来。

异步不是“不用等”,而是把等待换成了状态、监控和补偿。

二、异步解耦会让主链路变轻,也会让排查变难

订单创建后,可能要发通知、加积分、同步 CRM、更新报表。

如果这些动作都放在下单接口里同步执行,接口会越来越慢,也越来越容易被某个非核心环节拖垮。

用 MQ 后,订单服务只发布事件,其他服务订阅处理。主链路变轻了,模块之间也更独立。

代价是链路变长了。

以前一个接口失败就知道失败。现在可能是消息没发出去、消费者没消费、消费失败重试、下游处理了一半、补偿任务还没跑。

所以用了 MQ,就必须配套日志、追踪、重试、死信和补偿。异步不是不用管了,而是要换一种方式管。

三、重复消费一定要当成默认情况

第一次用 MQ 时,很容易希望“一条消息只消费一次”。

工程上更稳的想法是:重复消费一定会发生。

网络抖动、消费者超时、ack 失败、服务重启、分区重平衡,都可能导致同一条消息被再次投递。可靠系统不应该依赖“消息绝不重复”,而应该让消费逻辑幂等。

比如:

  • 用业务唯一号去重。
  • 处理前检查业务状态。
  • 用唯一索引防重复写入。
  • 记录消费日志。
  • 更新时带版本号或状态机约束。

扣款、发券、加积分这类操作如果没有幂等保护,重复消费一次就可能造成真实损失。

四、消息丢失和消息积压都要提前设计

消息丢失通常出现在三个地方:生产者没发成功,MQ 没持久化成功,消费者处理失败但错误 ack。

关键业务里,生产端要确认发送结果,MQ 要开启持久化和副本机制,消费端要在业务处理成功后再 ack。

更重要的业务,可以用本地消息表或 Outbox,把业务数据和待发送消息放在同一个本地事务里,再由后台任务可靠投递。

消息积压则是另一种压力反馈。

积压时不要只盲目加消费者。先看原因:是消费逻辑慢、数据库慢、下游接口慢,还是某类异常消息一直重试。

有时候加消费者会把下游打得更惨。真正要做的是找到瓶颈,再决定扩容、限流、降级、拆 Topic,还是临时跳过低优先级任务。

五、顺序要求越强,吞吐越容易被锁住

顺序消息听起来很美,但它会牺牲并发能力。

如果要求全局顺序,基本就只能串行处理,吞吐会很低。实际项目里更多是局部顺序:同一个订单、同一个用户、同一个设备的消息要按顺序处理。

这时可以按业务 ID 分区,让同一个业务对象的消息进入同一个队列或分区。

不要轻易说“所有消息都要顺序”。先问清楚:到底是哪一类业务对象需要顺序?

六、可以带走的判断清单

  • MQ 适合削峰和解耦,但前提是业务允许异步。
  • 异步链路要配套日志、追踪、重试和补偿。
  • 消费端一定要幂等。
  • 关键消息要考虑本地消息表或 Outbox。
  • 消息积压时先找瓶颈,不要只加消费者。
  • 顺序消息尽量做局部顺序,不要追求全局顺序。

MQ 不会让压力消失,它只是把压力换了一个地方。


本系列:

高并发海量数据系统架构 第 5 篇 / 共 11 篇
查看专题目录

留下一条评论

暂无评论