跳转至

消息队列面试场景问答

一、如何保证消息不被重复消费?

MQ 通常保证 At least once,可能重复。解决:消费端幂等

常见做法:

  1. 唯一业务 ID:消息带 bizId,消费前查 Redis:
    if (redis.setIfAbsent("msg:" + bizId, "1", 24h)) {
        doConsume(msg);
    } else {
        // 已经消费过,直接 ACK
    }
    
  2. 数据库唯一索引:插入重复主键会失败。
  3. 状态机:订单只有 INIT -> PAID 才能推进,重复消息直接忽略。

二、如何保证消息不丢失?

三段保障:

  1. 生产端:同步发送 + 重试,确认 Broker 收到。
  2. Broker:持久化到磁盘 + 主从复制。
  3. 消费端:业务处理完再 ACK,不要先 ACK 再处理。

反例

消费者收到消息立刻 ACK,然后业务处理时宕机,这条消息就丢了。

三、如何保证消息顺序?

  • 同一业务 key(如 orderId)hash 到同一 Queue。
  • 单 Queue 单线程消费。
  • RocketMQ 用 MessageQueueSelector
    producer.send(msg, (list, m, arg) -> {
        int idx = Math.abs(arg.hashCode()) % list.size();
        return list.get(idx);
    }, orderId);
    

注意:局部顺序可以做到,全局顺序代价极大(单 Queue 串行),一般不做。

四、消息积压怎么办?

  1. 先看是生产太快还是消费太慢。
  2. 消费慢:临时扩容消费者实例数(注意不能超过 Queue 数)。
  3. 批量消费、异步处理。
  4. 实在处理不过来,先转储到新 Topic,慢慢消费。
  5. 紧急情况:丢弃非核心消息,保住主流程。

五、延迟消息怎么实现?

  • RocketMQ:内置 18 个延迟级别(1s、5s、10s...2h)。
  • Kafka:没有原生支持,用时间轮 / 外部调度。
  • RabbitMQ:TTL + DLX。

六、MQ 如何选型?

Kafka RocketMQ RabbitMQ
吞吐量 极高(百万级) 高(十万级) 中(万级)
延迟 ms 级 ms 级 us 级
顺序 Partition 内顺序 Queue 内顺序 Queue 内顺序
延迟消息 不支持原生 支持 TTL+DLX
事务消息 支持(弱) 支持 不支持
适用 大数据、日志 电商、业务 中小规模、路由复杂

七、为什么用 MQ?

  1. 异步:下单后发短信、积分,主流程不等待。
  2. 削峰:秒杀流量先入 MQ,消费者按能力消费。
  3. 解耦:系统间不直接调用,通过 MQ 通信。

一句话总结

MQ 不是银弹,引入它就要接受:异步带来的复杂性、重复消费、消息丢失、顺序、积压这些问题。