消费者幂等设计:重复消息为什么不可避免
重复消息是消息系统里的常态,消费者必须通过业务唯一键、去重表、状态机和条件更新保证幂等。
消费者幂等设计:重复消息为什么不可避免
只要系统允许重试,就要接受重复消息。
生产者可能因为超时重发。消费者可能处理成功但提交 Offset 失败。Broker 可能重新投递未确认消息。消费者 Rebalance 也可能让某些消息再次被处理。
所以消费者必须幂等。
Rendering diagram...
用事件 ID 去重
最直接的方式是去重表。
CREATE TABLE message_dedup (
message_id VARCHAR(128) PRIMARY KEY,
consumer_group VARCHAR(128) NOT NULL,
processed_at TIMESTAMP NOT NULL
);
处理前插入去重记录。如果唯一键冲突,说明消息已经处理过。
用业务唯一键
有些场景不需要单独去重表,业务表本身可以兜底。
比如支付流水可以用支付单号做唯一键。
INSERT INTO payment_flows(payment_no, order_id, amount)
VALUES ('pay_10001', 10001, 199.00)
ON CONFLICT (payment_no) DO NOTHING;
同一支付事件重复消费时,不会重复写流水。
用状态机
订单状态机也能防止重复和乱序。
UPDATE orders
SET status = 'PAID'
WHERE id = 10001
AND status = 'CREATED';
如果订单已经是 PAID,重复的支付成功消息不会再次修改。
幂等不是只防重复
幂等还要防乱序。比如先收到 OrderCancelled,后收到迟到的 OrderPaid。如果状态机不限制,就可能把已取消订单改成已支付。
所以幂等设计通常要和状态机一起做。
小结
消费者幂等是消息系统的底线能力。不要假设消息只会来一次,要默认它可能重复、迟到、乱序,然后用唯一键、去重表和状态机兜住。