真实面经题目 · 原创解析
消费者端幂等性如何实现?
这题考察至少一次投递下的消费安全,核心是用业务唯一键、可靠去重记录和状态机把重复消息变成无副作用。
真实面经题目 · 原创解析
这题考察至少一次投递下的消费安全,核心是用业务唯一键、可靠去重记录和状态机把重复消息变成无副作用。
MQ 常见语义是至少一次投递,网络超时、ack 失败、消费者重启、重平衡和生产者重发都会导致重复。消费者幂等要用业务唯一键而不是盲目信任 messageId,典型流程是消费消息后开启数据库事务,先用 `business_key` 插入消费记录表或依赖业务表唯一约束,插入成功才执行业务状态变更,业务提交成功后再 ack;如果重复插入冲突,就直接按已处理返回。状态变更还要通过状态机、乐观锁或版本号保证只能从合法前置状态推进。
消费者处理成功但 ack 丢失、消费超时、rebalance、Broker 重投、生产端重试,都可能让同一业务事件再次到达。幂等不是优化项,而是消费端基本能力。
messageId 可能因重发而变化,不能总代表同一业务操作。订单号、支付单号、请求号、事件唯一 ID 这类业务 key 更可靠。
消费记录表插入和业务状态修改最好在同一个本地事务中提交。否则去重成功但业务失败,或业务成功但去重丢失,都会留下难以恢复的不一致。
扣款、发货、退款等业务要校验当前状态,只允许 `待支付 -> 已支付` 这类合法转移。重复消息到来时发现状态已推进,应安全返回而不是再次执行副作用。
不能先 ack 再提交业务,否则崩溃会丢消息。正确做法是业务事务提交后 ack;ack 失败导致重投时,由幂等记录和状态机兜住重复。
轻量场景可以,但要考虑 TTL、Redis 持久化、缓存丢失和业务 DB 事务不一致。关键资金或订单状态更适合数据库唯一约束或消费表。
消息会重投,所以业务必须能重复执行而无副作用。重复到达时命中消费表或状态机,直接返回成功并再次 ack。
生产端重试、业务补发或跨系统转发可能产生不同 messageId,但业务上是同一操作。幂等键应该来自业务唯一语义。
会,所以要按业务保留期清理、按时间分区或归档。清理窗口必须大于消息最大重试和补偿周期。